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