github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/libkb/passphrase_stream_store.go (about)

     1  // Copyright 2019 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package libkb
     5  
     6  // Code in this file drives a temporary storage for PWHash and EdDSA parts of
     7  // passphrase stream. If signup is done with GenerateRandomPassphrase=true and
     8  // it fails, but after SignupJoin has already succeeded (so account has been
     9  // created, but it has no sigchain or devices), we store PWHash and EdDSA parts
    10  // of passphrase stream using additional two secret store entries. Each part is
    11  // 32-bytes so they fit using existing secret store code: we pretend these are
    12  // LKSecFullSecret.
    13  
    14  // This partial passphrase stream is then used during login to let the user in
    15  // and continue signup process and provision their first device. Normally they
    16  // would be able to do that by entering their password, but since it was a
    17  // GenerateRandomPassphrase (or NOPW) signup, they don't know it. Both EdDSA
    18  // and PWHash entries are then cleared after provisioning succeeds.
    19  
    20  import (
    21  	"fmt"
    22  	"strings"
    23  
    24  	"github.com/keybase/client/go/protocol/keybase1"
    25  )
    26  
    27  // pwhStoreIdentifier is the suffix used for secret store identifier.
    28  type pwhStoreIdentifier string
    29  
    30  const (
    31  	ssEddsaSuffix  pwhStoreIdentifier = "tmp_eddsa"
    32  	ssPwhashSuffix pwhStoreIdentifier = "tmp_pwhash"
    33  )
    34  
    35  func formatPPSSecretStoreIdentifier(username NormalizedUsername, typ pwhStoreIdentifier) NormalizedUsername {
    36  	return NormalizedUsername(fmt.Sprintf("%s.%s", username, typ))
    37  }
    38  
    39  func isPPSSecretStore(identifier string) bool {
    40  	return strings.HasSuffix(identifier, string(ssEddsaSuffix)) ||
    41  		strings.HasSuffix(identifier, string(ssPwhashSuffix))
    42  }
    43  
    44  func RetrievePwhashEddsaPassphraseStream(mctx MetaContext, username NormalizedUsername, uid keybase1.UID) (ret *PassphraseStream, err error) {
    45  	defer mctx.Trace(fmt.Sprintf("RetrievePwhashEddsaPassphraseStream(%q,%q)", username, uid),
    46  		&err)()
    47  
    48  	ss := mctx.G().SecretStore()
    49  
    50  	var pwHash passphraseStreamPWHash
    51  	pwHashSecret, err := ss.RetrieveSecret(mctx, formatPPSSecretStoreIdentifier(username, ssPwhashSuffix))
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	copy(pwHash[:], pwHashSecret.Bytes())
    56  
    57  	var eddsaSeed passphraseSteramEdDSASeed
    58  	edDSASecret, err := ss.RetrieveSecret(mctx, formatPPSSecretStoreIdentifier(username, ssEddsaSuffix))
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	copy(eddsaSeed[:], edDSASecret.Bytes())
    63  
    64  	pps := newPassphraseStreamFromPwhAndEddsa(pwHash, eddsaSeed)
    65  	return pps, nil
    66  }
    67  
    68  func StorePwhashEddsaPassphraseStream(mctx MetaContext, username NormalizedUsername, pps *PassphraseStream) (err error) {
    69  	defer mctx.Trace(fmt.Sprintf("StorePwhashEddsaPassphraseStream(%q)", username),
    70  		&err)()
    71  
    72  	ss := mctx.G().SecretStore()
    73  
    74  	var secret LKSecFullSecret
    75  	var id NormalizedUsername
    76  
    77  	prevOptions := ss.GetOptions(mctx)
    78  	ss.SetOptions(mctx, &SecretStoreOptions{RandomPw: true})
    79  	// Restore secret store options after we are done here.
    80  	defer ss.SetOptions(mctx, prevOptions)
    81  
    82  	defer func() {
    83  		if err != nil {
    84  			// Never store partial secret.
    85  			mctx.Debug("Clearing partial secret after unsuccessful store, error was: %s", err)
    86  			clrErr := ClearPwhashEddsaPassphraseStream(mctx, username)
    87  			if clrErr != nil {
    88  				mctx.Debug("Failed to clear: %v", clrErr)
    89  			}
    90  		}
    91  	}()
    92  
    93  	secret, err = newLKSecFullSecretFromBytes(pps.EdDSASeed())
    94  	if err != nil {
    95  		return err
    96  	}
    97  	id = formatPPSSecretStoreIdentifier(username, ssEddsaSuffix)
    98  	if err := ss.StoreSecret(mctx, id, secret); err != nil {
    99  		return err
   100  	}
   101  
   102  	secret, err = newLKSecFullSecretFromBytes(pps.PWHash())
   103  	if err != nil {
   104  		return err
   105  	}
   106  	id = formatPPSSecretStoreIdentifier(username, ssPwhashSuffix)
   107  	if err := ss.StoreSecret(mctx, id, secret); err != nil {
   108  		return err
   109  	}
   110  
   111  	return nil
   112  }
   113  
   114  func ClearPwhashEddsaPassphraseStream(mctx MetaContext, username NormalizedUsername) error {
   115  	ss := mctx.G().SecretStore()
   116  
   117  	prevOptions := ss.GetOptions(mctx)
   118  	ss.SetOptions(mctx, &SecretStoreOptions{RandomPw: true})
   119  	// Restore secret store options after we are done here.
   120  	defer ss.SetOptions(mctx, prevOptions)
   121  
   122  	err1 := ss.ClearSecret(mctx, formatPPSSecretStoreIdentifier(username, ssEddsaSuffix))
   123  	err2 := ss.ClearSecret(mctx, formatPPSSecretStoreIdentifier(username, ssPwhashSuffix))
   124  	return CombineErrors(err1, err2)
   125  }