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

     1  // Copyright 2018 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  //go:build android
     5  // +build android
     6  
     7  package libkb
     8  
     9  import (
    10  	"errors"
    11  	"sync"
    12  
    13  	"github.com/keybase/client/go/kbcrypto"
    14  	"github.com/keybase/client/go/msgpack"
    15  )
    16  
    17  // UnsafeExternalKeyStore is a simple interface that external clients can implement.
    18  // It is unsafe because it returns raw bytes instead of the typed LKSecFullSecret
    19  // Use with TypeSafeExternalKeyStoreProxy
    20  type UnsafeExternalKeyStore interface {
    21  	RetrieveSecret(serviceName string, key string) ([]byte, error)
    22  	StoreSecret(serviceName string, key string, secret []byte) error
    23  	ClearSecret(serviceName string, key string) error
    24  	GetUsersWithStoredSecretsMsgPack(serviceName string) ([]byte, error)
    25  	SetupKeyStore(serviceName string, key string) error
    26  }
    27  
    28  // ExternalKeyStore is the interface for the actual (external) keystore.
    29  type ExternalKeyStore interface {
    30  	RetrieveSecret(serviceName string, key string) (LKSecFullSecret, error)
    31  	StoreSecret(serviceName string, key string, secret LKSecFullSecret) error
    32  	ClearSecret(serviceName string, key string) error
    33  	GetUsersWithStoredSecretsMsgPack(serviceName string) ([]byte, error)
    34  	SetupKeyStore(serviceName string, key string) error
    35  }
    36  
    37  // TypeSafeExternalKeyStoreProxy wraps the UnsafeExternalKeyStore to provide
    38  // the type-safe ExternalKeyStore interface to the rest of the code
    39  type TypeSafeExternalKeyStoreProxy struct {
    40  	UnsafeExternalKeyStore UnsafeExternalKeyStore
    41  }
    42  
    43  func (w TypeSafeExternalKeyStoreProxy) RetrieveSecret(serviceName string, key string) (LKSecFullSecret, error) {
    44  	bytes, err := w.UnsafeExternalKeyStore.RetrieveSecret(serviceName, key)
    45  	if err != nil {
    46  		return LKSecFullSecret{}, err
    47  	}
    48  
    49  	return newLKSecFullSecretFromBytes(bytes)
    50  }
    51  
    52  func (w TypeSafeExternalKeyStoreProxy) StoreSecret(serviceName string, key string, secret LKSecFullSecret) error {
    53  	return w.UnsafeExternalKeyStore.StoreSecret(serviceName, key, secret.Bytes())
    54  }
    55  
    56  func (w TypeSafeExternalKeyStoreProxy) ClearSecret(serviceName string, key string) error {
    57  	return w.UnsafeExternalKeyStore.ClearSecret(serviceName, key)
    58  }
    59  
    60  func (w TypeSafeExternalKeyStoreProxy) GetUsersWithStoredSecretsMsgPack(serviceName string) ([]byte, error) {
    61  	return w.UnsafeExternalKeyStore.GetUsersWithStoredSecretsMsgPack(serviceName)
    62  }
    63  
    64  func (w TypeSafeExternalKeyStoreProxy) SetupKeyStore(serviceName string, key string) error {
    65  	return w.UnsafeExternalKeyStore.SetupKeyStore(serviceName, key)
    66  }
    67  
    68  // externalKeyStore is the reference to some external key store
    69  var externalKeyStore ExternalKeyStore
    70  var externalKeyStoreInitialized bool
    71  var externalKeyStoreMu sync.Mutex
    72  
    73  // SetGlobalExternalKeyStore is called by Android to register Android's KeyStore with Go
    74  func SetGlobalExternalKeyStore(s UnsafeExternalKeyStore) {
    75  	externalKeyStoreMu.Lock()
    76  	defer externalKeyStoreMu.Unlock()
    77  	externalKeyStore = TypeSafeExternalKeyStoreProxy{s}
    78  	externalKeyStoreInitialized = false
    79  }
    80  
    81  var errNoExternalKeyStore = errors.New("no external key store available")
    82  
    83  func getGlobalExternalKeyStore(m MetaContext) (ExternalKeyStore, error) {
    84  	externalKeyStoreMu.Lock()
    85  	defer externalKeyStoreMu.Unlock()
    86  
    87  	if externalKeyStore == nil {
    88  		// perhaps SetGlobalExternalKeyStore has not been called by Android internals yet:
    89  		m.Debug("secret_store_external:getGlobalExternalKeyStore called, but externalKeyStore is nil")
    90  		return nil, errNoExternalKeyStore
    91  	}
    92  
    93  	// always check this since perhaps SetGlobalExternalKeyStore called more than once
    94  	if !externalKeyStoreInitialized {
    95  		m.Debug("+ secret_store_external:setup (in getGlobalExternalKeyStore)")
    96  		defer m.Debug("- secret_store_external:setup (in getGlobalExternalKeyStore)")
    97  
    98  		serviceName := m.G().GetStoredSecretServiceName()
    99  
   100  		// username not required
   101  		err := externalKeyStore.SetupKeyStore(serviceName, "")
   102  		if err != nil {
   103  			m.Debug("externalKeyStore.SetupKeyStore(%s) error: %s (%T)", serviceName, err, err)
   104  			return nil, err
   105  		}
   106  
   107  		m.Debug("externalKeyStore.SetupKeyStore(%s) success", serviceName)
   108  		externalKeyStoreInitialized = true
   109  	}
   110  
   111  	return externalKeyStore, nil
   112  }
   113  
   114  type secretStoreAndroid struct{}
   115  
   116  var _ SecretStoreAll = &secretStoreAndroid{}
   117  
   118  func (s *secretStoreAndroid) serviceName(m MetaContext) string {
   119  	return m.G().GetStoredSecretServiceName()
   120  }
   121  
   122  func (s *secretStoreAndroid) StoreSecret(m MetaContext, username NormalizedUsername, secret LKSecFullSecret) (err error) {
   123  	defer m.Trace("secret_store_external StoreSecret", &err)()
   124  	ks, err := getGlobalExternalKeyStore(m)
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	return ks.StoreSecret(s.serviceName(m), string(username), secret)
   130  }
   131  
   132  func (s *secretStoreAndroid) RetrieveSecret(m MetaContext, username NormalizedUsername) (sec LKSecFullSecret, err error) {
   133  	defer m.Trace("secret_store_external RetrieveSecret", &err)()
   134  
   135  	ks, err := getGlobalExternalKeyStore(m)
   136  	if err != nil {
   137  		return sec, err
   138  	}
   139  
   140  	return ks.RetrieveSecret(s.serviceName(m), string(username))
   141  }
   142  
   143  func (s *secretStoreAndroid) ClearSecret(m MetaContext, username NormalizedUsername) (err error) {
   144  	defer m.Trace("secret_store_external ClearSecret", &err)()
   145  	if username.IsNil() {
   146  		m.Debug("NOOPing secretStoreAndroid#ClearSecret for empty username")
   147  		return nil
   148  	}
   149  	ks, err := getGlobalExternalKeyStore(m)
   150  	if err != nil {
   151  		return err
   152  	}
   153  
   154  	return ks.ClearSecret(s.serviceName(m), string(username))
   155  }
   156  
   157  func (s *secretStoreAndroid) GetUsersWithStoredSecrets(m MetaContext) (users []string, err error) {
   158  	defer m.Trace("secret_store_external GetUsersWithStoredSecrets", &err)()
   159  
   160  	ks, err := getGlobalExternalKeyStore(m)
   161  	if err != nil {
   162  		if err == errNoExternalKeyStore {
   163  			// this is to match previous behavior of this function,
   164  			// but perhaps it should return the error instead
   165  			return nil, nil
   166  		}
   167  		return nil, err
   168  	}
   169  	usersMsgPack, err := ks.GetUsersWithStoredSecretsMsgPack(s.serviceName(m))
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  	ch := kbcrypto.CodecHandle()
   174  	var usersUnpacked []string
   175  	err = msgpack.DecodeAll(usersMsgPack, ch, &usersUnpacked)
   176  	for _, v := range usersUnpacked {
   177  		if !isPPSSecretStore(v) {
   178  			users = append(users, v)
   179  		}
   180  	}
   181  	return users, err
   182  }
   183  
   184  func (s *secretStoreAndroid) GetOptions(MetaContext) *SecretStoreOptions  { return nil }
   185  func (s *secretStoreAndroid) SetOptions(MetaContext, *SecretStoreOptions) {}