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	"runtime/debug"
9
10	keybase1 "github.com/keybase/client/go/protocol/keybase1"
11	triplesec "github.com/keybase/go-triplesec"
12)
13
14func NewSecureTriplesec(passphrase []byte, salt []byte) (Triplesec, error) {
15	return triplesec.NewCipher(passphrase, salt, ClientTriplesecVersion)
16}
17
18func StretchPassphrase(g *GlobalContext, passphrase string, salt []byte) (tsec Triplesec, pps *PassphraseStream, err error) {
19	if salt == nil {
20		err = fmt.Errorf("no salt provided to StretchPassphrase")
21		return nil, nil, err
22	}
23	var tmp []byte
24	var fn func(pw []byte, salt []byte) (Triplesec, error)
25
26	// free memory on mobile before we do this to reduce chance that we get killed because of the
27	// large scrypt allocation coming
28	if g != nil && g.IsMobileAppType() {
29		debug.FreeOSMemory()
30	}
31	if g == nil {
32		fn = NewSecureTriplesec
33	} else {
34		fn = g.NewTriplesec
35	}
36
37	tsec, err = fn([]byte(passphrase), salt)
38	if err != nil {
39		return nil, nil, err
40	}
41	_, tmp, err = tsec.DeriveKey(extraLen)
42	if err != nil {
43		return nil, nil, err
44	}
45	pps = NewPassphraseStream(tmp)
46	return tsec, pps, nil
47}
48
49const (
50	pwhIndex   = 0
51	pwhLen     = 32
52	eddsaIndex = pwhIndex + pwhLen
53	eddsaLen   = 32
54	dhIndex    = eddsaIndex + eddsaLen
55	dhLen      = 32
56	lksIndex   = dhIndex + dhLen
57	lksLen     = LKSecLen // == 32
58	extraLen   = pwhLen + eddsaLen + dhLen + lksLen
59)
60
61type PassphraseStream struct {
62	stream []byte
63	gen    PassphraseGeneration
64}
65
66func NewPassphraseStream(s []byte) *PassphraseStream {
67	return &PassphraseStream{
68		stream: s,
69		gen:    PassphraseGeneration(0),
70	}
71}
72
73// NewPassphraseStreamLKSecOnly creates a PassphraseStream only with the lks bytes
74// (stream[lksIndex:]).  The rest of the stream is zeros.
75// This is used to create a passphrase stream from the information in the
76// secret store, which only contains the lksec portion of the stream.
77func NewPassphraseStreamLKSecOnly(s *LKSec) (*PassphraseStream, error) {
78
79	clientHalf, err := s.ComputeClientHalf()
80	if err != nil {
81		return nil, err
82	}
83	stream := make([]byte, extraLen)
84	copy(stream[lksIndex:], clientHalf.Bytes())
85	ps := &PassphraseStream{
86		stream: stream,
87		gen:    s.Generation(),
88	}
89	return ps, nil
90}
91
92func (ps *PassphraseStream) SetGeneration(gen PassphraseGeneration) {
93	ps.gen = gen
94}
95
96type passphraseStreamPWHash [pwhLen]byte
97type passphraseSteramEdDSASeed [eddsaLen]byte
98
99func newPassphraseStreamFromPwhAndEddsa(pwhash passphraseStreamPWHash, eddsa passphraseSteramEdDSASeed) *PassphraseStream {
100	stream := make([]byte, extraLen)
101	copy(stream[pwhIndex:eddsaIndex], pwhash[:])
102	copy(stream[eddsaIndex:dhIndex], eddsa[:])
103	ps := &PassphraseStream{
104		stream: stream,
105		gen:    PassphraseGeneration(0),
106	}
107	return ps
108}
109
110func (ps PassphraseStream) PWHash() []byte {
111	return ps.stream[pwhIndex:eddsaIndex]
112}
113
114func (ps PassphraseStream) EdDSASeed() []byte {
115	return ps.stream[eddsaIndex:dhIndex]
116}
117
118func (ps PassphraseStream) DHSeed() []byte {
119	return ps.stream[dhIndex:lksIndex]
120}
121
122func (ps PassphraseStream) LksClientHalf() LKSecClientHalf {
123	ret, _ := NewLKSecClientHalfFromBytes(ps.stream[lksIndex:])
124	return ret
125}
126
127func (ps PassphraseStream) ToLKSec(uid keybase1.UID) (*LKSec, error) {
128	ch, err := NewLKSecClientHalfFromBytes(ps.stream[lksIndex:])
129	if err != nil {
130		return nil, err
131	}
132	return &LKSec{
133		clientHalf: ch,
134		ppGen:      ps.Generation(),
135		uid:        uid,
136	}, nil
137}
138
139func (ps PassphraseStream) PDPKA5KID() (keybase1.KID, error) {
140	return seedToPDPKAKID(ps.EdDSASeed())
141}
142
143func (ps PassphraseStream) String() string {
144	return fmt.Sprintf("pwh:   %x\nEdDSA: %x\nDH:    %x\nlks:   %x",
145		ps.PWHash(), ps.EdDSASeed(), ps.DHSeed(), ps.LksClientHalf().Bytes())
146}
147
148// Generation returns the generation of this passphrase stream.
149// It is >=0 for valid generation #.  If 0, then we assume the
150// passphrase has never been reset.
151func (ps PassphraseStream) Generation() PassphraseGeneration {
152	return ps.gen
153}
154
155// Clone a passphrase stream and return a copy.
156func (ps *PassphraseStream) Clone() *PassphraseStream {
157	if ps == nil {
158		return nil
159	}
160	arr := make([]byte, len(ps.stream))
161	copy(arr, ps.stream)
162	return &PassphraseStream{
163		stream: arr,
164		gen:    ps.gen,
165	}
166}
167
168func (ps PassphraseStream) Export() keybase1.PassphraseStream {
169	return keybase1.PassphraseStream{
170		PassphraseStream: ps.stream,
171		Generation:       int(ps.gen),
172	}
173}
174
175func (ps PassphraseStream) SyncAndCheckIfOutdated(mctx MetaContext) (bool, error) {
176	ss, err := mctx.SyncSecrets()
177	if err != nil {
178		return false, err
179	}
180
181	key, err := ss.FindDevice(mctx.G().Env.GetDeviceID())
182	if err != nil {
183		return false, err
184	}
185
186	return key.PPGen > ps.Generation(), nil
187}
188