github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/passphrase_stream.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package libkb
     5  
     6  import (
     7  	"fmt"
     8  	"runtime/debug"
     9  
    10  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    11  	triplesec "github.com/keybase/go-triplesec"
    12  )
    13  
    14  func NewSecureTriplesec(passphrase []byte, salt []byte) (Triplesec, error) {
    15  	return triplesec.NewCipher(passphrase, salt, ClientTriplesecVersion)
    16  }
    17  
    18  func 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  
    49  const (
    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  
    61  type PassphraseStream struct {
    62  	stream []byte
    63  	gen    PassphraseGeneration
    64  }
    65  
    66  func 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.
    77  func 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  
    92  func (ps *PassphraseStream) SetGeneration(gen PassphraseGeneration) {
    93  	ps.gen = gen
    94  }
    95  
    96  type passphraseStreamPWHash [pwhLen]byte
    97  type passphraseSteramEdDSASeed [eddsaLen]byte
    98  
    99  func 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  
   110  func (ps PassphraseStream) PWHash() []byte {
   111  	return ps.stream[pwhIndex:eddsaIndex]
   112  }
   113  
   114  func (ps PassphraseStream) EdDSASeed() []byte {
   115  	return ps.stream[eddsaIndex:dhIndex]
   116  }
   117  
   118  func (ps PassphraseStream) DHSeed() []byte {
   119  	return ps.stream[dhIndex:lksIndex]
   120  }
   121  
   122  func (ps PassphraseStream) LksClientHalf() LKSecClientHalf {
   123  	ret, _ := NewLKSecClientHalfFromBytes(ps.stream[lksIndex:])
   124  	return ret
   125  }
   126  
   127  func (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  
   139  func (ps PassphraseStream) PDPKA5KID() (keybase1.KID, error) {
   140  	return seedToPDPKAKID(ps.EdDSASeed())
   141  }
   142  
   143  func (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.
   151  func (ps PassphraseStream) Generation() PassphraseGeneration {
   152  	return ps.gen
   153  }
   154  
   155  // Clone a passphrase stream and return a copy.
   156  func (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  
   168  func (ps PassphraseStream) Export() keybase1.PassphraseStream {
   169  	return keybase1.PassphraseStream{
   170  		PassphraseStream: ps.stream,
   171  		Generation:       int(ps.gen),
   172  	}
   173  }
   174  
   175  func (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  }