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

     1  package libkb
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/keybase/client/go/kbcrypto"
    12  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    13  	"github.com/keybase/go-codec/codec"
    14  	context "golang.org/x/net/context"
    15  )
    16  
    17  type SKBKeyringFile struct {
    18  	Contextified
    19  	sync.Mutex
    20  	username NormalizedUsername
    21  	filename string
    22  	Blocks   []*SKB
    23  	fpIndex  map[PGPFingerprint]*SKB
    24  	kidIndex map[keybase1.KID]*SKB
    25  	dirty    bool
    26  }
    27  
    28  func NewSKBKeyringFile(g *GlobalContext, un NormalizedUsername) *SKBKeyringFile {
    29  	return &SKBKeyringFile{
    30  		Contextified: NewContextified(g),
    31  		username:     un,
    32  		filename:     g.SKBFilenameForUser(un),
    33  		fpIndex:      make(map[PGPFingerprint]*SKB),
    34  		kidIndex:     make(map[keybase1.KID]*SKB),
    35  		dirty:        false,
    36  	}
    37  }
    38  
    39  func (k *SKBKeyringFile) Load(ctx context.Context) (err error) {
    40  	k.Lock()
    41  	defer k.Unlock()
    42  	return k.loadLocked(ctx)
    43  }
    44  
    45  func (k *SKBKeyringFile) Username() NormalizedUsername {
    46  	return k.username
    47  }
    48  
    49  func (k *SKBKeyringFile) IsForUsername(un NormalizedUsername) bool {
    50  	return k.username.Eq(un)
    51  }
    52  
    53  func (k *SKBKeyringFile) MTime() (mtime time.Time, err error) {
    54  	k.Lock()
    55  	defer k.Unlock()
    56  	var fi os.FileInfo
    57  	fi, err = os.Stat(k.filename)
    58  	if err != nil {
    59  		return mtime, err
    60  	}
    61  	return fi.ModTime(), err
    62  }
    63  
    64  func (k *SKBKeyringFile) MarkDirty() {
    65  	k.Lock()
    66  	defer k.Unlock()
    67  	k.dirty = true
    68  }
    69  
    70  type skbPacket struct {
    71  	skb *SKB
    72  }
    73  
    74  // Okay to panic in Codec{Encode,Decode}Self, since the
    75  // encoder/decoder catches panics and turns them back into errors.
    76  
    77  func (s *skbPacket) CodecEncodeSelf(e *codec.Encoder) {
    78  	err := kbcrypto.EncodePacket(s.skb, e)
    79  	if err != nil {
    80  		panic(err)
    81  	}
    82  }
    83  
    84  func (s *skbPacket) CodecDecodeSelf(d *codec.Decoder) {
    85  	var skb SKB
    86  	err := kbcrypto.DecodePacket(d, &skb)
    87  	if err != nil {
    88  		panic(err)
    89  	}
    90  	s.skb = &skb
    91  }
    92  
    93  func encodeSKBPacketList(skbs []*SKB, w io.Writer) error {
    94  	ch := kbcrypto.CodecHandle()
    95  	encoder := codec.NewEncoder(w, ch)
    96  
    97  	packets := make([]skbPacket, len(skbs))
    98  	for i := range skbs {
    99  		packets[i].skb = skbs[i]
   100  	}
   101  
   102  	return encoder.Encode(packets)
   103  }
   104  
   105  func decodeSKBPacketList(r io.Reader, g *GlobalContext) ([]*SKB, error) {
   106  	ch := kbcrypto.CodecHandle()
   107  	decoder := codec.NewDecoder(r, ch)
   108  
   109  	var packets []skbPacket
   110  	err := decoder.Decode(&packets)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	skbs := make([]*SKB, len(packets))
   116  	for i, s := range packets {
   117  		s.skb.SetGlobalContext(g)
   118  		skbs[i] = s.skb
   119  	}
   120  	return skbs, nil
   121  }
   122  
   123  func (k *SKBKeyringFile) loadLocked(ctx context.Context) (err error) {
   124  	k.G().Log.CDebugf(ctx, "+ Loading SKB keyring: %s", k.filename)
   125  
   126  	file, err := os.OpenFile(k.filename, os.O_RDONLY, 0)
   127  	if err != nil {
   128  		if os.IsNotExist(err) {
   129  			k.G().Log.CDebugf(ctx, "| Keybase secret keyring doesn't exist: %s", k.filename)
   130  		} else {
   131  			k.G().Log.CWarningf(ctx, "Error opening %s: %s", k.filename, err)
   132  
   133  			MobilePermissionDeniedCheck(k.G(), err, fmt.Sprintf("skb keyring: %s", k.filename))
   134  		}
   135  		return err
   136  	}
   137  	defer func() {
   138  		closeErr := file.Close()
   139  		if err == nil {
   140  			err = closeErr
   141  		}
   142  	}()
   143  
   144  	stream := base64.NewDecoder(base64.StdEncoding, file)
   145  	skbs, err := decodeSKBPacketList(stream, k.G())
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	k.Blocks = skbs
   151  
   152  	k.G().Log.CDebugf(ctx, "- Loaded SKB keyring: %s -> %s", k.filename, ErrToOk(err))
   153  	return nil
   154  }
   155  
   156  func (k *SKBKeyringFile) addToIndexLocked(g GenericKey, b *SKB) {
   157  	if g == nil {
   158  		return
   159  	}
   160  	if fp := GetPGPFingerprintFromGenericKey(g); fp != nil {
   161  		k.fpIndex[*fp] = b
   162  	}
   163  	k.kidIndex[g.GetKID()] = b
   164  }
   165  
   166  func (k *SKBKeyringFile) Index() (err error) {
   167  	k.Lock()
   168  	defer k.Unlock()
   169  	return k.indexLocked()
   170  }
   171  
   172  func (k *SKBKeyringFile) indexLocked() (err error) {
   173  	for _, b := range k.Blocks {
   174  		var key GenericKey
   175  		key, err = b.GetPubKey()
   176  		if err != nil {
   177  			return
   178  		}
   179  		// Last-writer wins!
   180  		k.addToIndexLocked(key, b)
   181  	}
   182  	k.G().Log.Debug("| Indexed %d secret keys", len(k.Blocks))
   183  	return
   184  }
   185  
   186  func (k *SKBKeyringFile) SearchWithComputedKeyFamily(ckf *ComputedKeyFamily, ska SecretKeyArg) []*SKB {
   187  	k.Lock()
   188  	defer k.Unlock()
   189  
   190  	var kid keybase1.KID
   191  	k.G().Log.Debug("+ SKBKeyringFile.SearchWithComputedKeyFamily")
   192  	defer func() {
   193  		var res string
   194  		if kid.Exists() {
   195  			res = kid.String()
   196  		} else {
   197  			res = "<nil>"
   198  		}
   199  		k.G().Log.Debug("- SKBKeyringFile.SearchWithComputedKeyFamily -> %s\n", res)
   200  	}()
   201  	k.G().Log.Debug("| Searching %d possible blocks", len(k.Blocks))
   202  	var blocks []*SKB
   203  	for i := len(k.Blocks) - 1; i >= 0; i-- {
   204  		k.G().Log.Debug("| trying key index# -> %d", i)
   205  		if key, err := k.Blocks[i].GetPubKey(); err == nil && key != nil {
   206  			kid = key.GetKID()
   207  			active := ckf.GetKeyRole(kid)
   208  			k.G().Log.Debug("| Checking KID: %s -> %d", kid, int(active))
   209  			if !ska.KeyType.nonDeviceKeyMatches(key) {
   210  				k.G().Log.Debug("| Skipped, doesn't match type=%s", ska.KeyType)
   211  			} else if !KeyMatchesQuery(key, ska.KeyQuery, ska.ExactMatch) {
   212  				k.G().Log.Debug("| Skipped, doesn't match query=%s", ska.KeyQuery)
   213  
   214  			} else if active != DLGSibkey {
   215  				k.G().Log.Debug("| Skipped, active=%d", int(active))
   216  			} else {
   217  				blocks = append(blocks, k.Blocks[i])
   218  			}
   219  		} else {
   220  			k.G().Log.Debug("| failed --> %v", err)
   221  		}
   222  	}
   223  	return blocks
   224  }
   225  
   226  func (k *SKBKeyringFile) PushAndSave(skb *SKB) error {
   227  	k.Lock()
   228  	defer k.Unlock()
   229  	if err := k.pushLocked(skb); err != nil {
   230  		return err
   231  	}
   232  	return k.saveLocked()
   233  }
   234  
   235  func (k *SKBKeyringFile) HasPGPKeys() bool {
   236  	k.Lock()
   237  	defer k.Unlock()
   238  	return len(k.fpIndex) > 0
   239  }
   240  
   241  func (k *SKBKeyringFile) AllPGPBlocks() ([]*SKB, error) {
   242  	k.Lock()
   243  	defer k.Unlock()
   244  	var pgpBlocks []*SKB
   245  	for _, block := range k.Blocks {
   246  		k, err := block.GetPubKey()
   247  		if err != nil {
   248  			return nil, err
   249  		}
   250  		if fp := GetPGPFingerprintFromGenericKey(k); fp != nil {
   251  			pgpBlocks = append(pgpBlocks, block)
   252  		}
   253  	}
   254  	return pgpBlocks, nil
   255  }
   256  
   257  func (k *SKBKeyringFile) RemoveAllPGPBlocks() error {
   258  	k.Lock()
   259  	defer k.Unlock()
   260  	var blocks []*SKB
   261  	for _, block := range k.Blocks {
   262  		k, err := block.GetPubKey()
   263  		if err != nil {
   264  			return err
   265  		}
   266  		if fp := GetPGPFingerprintFromGenericKey(k); fp == nil {
   267  			blocks = append(blocks, block)
   268  		}
   269  	}
   270  	k.Blocks = blocks
   271  	k.fpIndex = make(map[PGPFingerprint]*SKB)
   272  	k.kidIndex = make(map[keybase1.KID]*SKB)
   273  	err := k.indexLocked()
   274  	if err != nil {
   275  		return err
   276  	}
   277  	k.dirty = true
   278  
   279  	return nil
   280  }
   281  
   282  func (k *SKBKeyringFile) Push(skb *SKB) error {
   283  	k.Lock()
   284  	defer k.Unlock()
   285  	return k.pushLocked(skb)
   286  }
   287  
   288  func (k *SKBKeyringFile) pushLocked(skb *SKB) error {
   289  	key, err := skb.GetPubKey()
   290  	if err != nil {
   291  		return fmt.Errorf("Failed to get pubkey: %s", err)
   292  	}
   293  	k.dirty = true
   294  	k.Blocks = append(k.Blocks, skb)
   295  	k.addToIndexLocked(key, skb)
   296  	return nil
   297  }
   298  
   299  func (k *SKBKeyringFile) Save() error {
   300  	k.Lock()
   301  	defer k.Unlock()
   302  	return k.saveLocked()
   303  }
   304  
   305  func (k *SKBKeyringFile) saveLocked() error {
   306  	if !k.dirty {
   307  		k.G().Log.Debug("SKBKeyringFile: saveLocked %s: not dirty, so skipping save", k.filename)
   308  		return nil
   309  	}
   310  	if err := MakeParentDirs(k.G().Log, k.filename); err != nil {
   311  		k.G().Log.Debug("SKBKeyringFile: saveLocked %s: failed to make parent dirs: %s", k.filename, err)
   312  		return err
   313  	}
   314  	k.G().Log.Debug("SKBKeyringFile: saveLocked %s: dirty, safe saving", k.filename)
   315  	if err := SafeWriteToFile(k.G().Log, k, 0); err != nil {
   316  		k.G().Log.Debug("SKBKeyringFile: saveLocked %s: SafeWriteToFile error: %s", k.filename, err)
   317  		return err
   318  	}
   319  	k.dirty = false
   320  	k.G().Log.Debug("SKBKeyringFile: saveLocked success for %s", k.filename)
   321  	return nil
   322  }
   323  
   324  func (k *SKBKeyringFile) LookupByFingerprint(fp PGPFingerprint) *SKB {
   325  	k.Lock()
   326  	defer k.Unlock()
   327  	ret, ok := k.fpIndex[fp]
   328  	if !ok {
   329  		ret = nil
   330  	}
   331  	return ret
   332  }
   333  
   334  // FindSecretKey will, given a list of KIDs, find the first one in the
   335  // list that has a corresponding secret key in the keyring file.
   336  func (k *SKBKeyringFile) FindSecretKey(kids []keybase1.KID) (ret *SKB) {
   337  	k.Lock()
   338  	defer k.Unlock()
   339  	for _, kid := range kids {
   340  		if ret = k.lookupByKidLocked(kid); ret != nil {
   341  			return
   342  		}
   343  	}
   344  	return
   345  }
   346  
   347  func (k *SKBKeyringFile) LookupByKid(kid keybase1.KID) *SKB {
   348  	k.Lock()
   349  	defer k.Unlock()
   350  	return k.lookupByKidLocked(kid)
   351  }
   352  
   353  func (k *SKBKeyringFile) lookupByKidLocked(kid keybase1.KID) *SKB {
   354  	ret, ok := k.kidIndex[kid]
   355  	if !ok {
   356  		ret = nil
   357  	}
   358  	return ret
   359  }
   360  
   361  func (k *SKBKeyringFile) LoadAndIndex(ctx context.Context) error {
   362  	k.Lock()
   363  	defer k.Unlock()
   364  	err := k.loadLocked(ctx)
   365  	if err == nil {
   366  		err = k.indexLocked()
   367  	}
   368  	return err
   369  }
   370  
   371  // GetFilename is only called from within Save(), so it's called
   372  // with a lock. Needs to be called GetFilename() to meet the interface
   373  // required by SafeSaveToFile
   374  func (k *SKBKeyringFile) GetFilename() string { return k.filename }
   375  
   376  // WriteTo is similar to GetFilename described just above in terms of
   377  // locking discipline.
   378  func (k *SKBKeyringFile) WriteTo(w io.Writer) (n int64, err error) {
   379  	k.G().Log.Debug("+ SKBKeyringFile WriteTo")
   380  	defer k.G().Log.Debug("- SKBKeyringFile WriteTo")
   381  	b64 := base64.NewEncoder(base64.StdEncoding, w)
   382  	defer func() {
   383  		// explicitly check for error on Close:
   384  		if closeErr := b64.Close(); closeErr != nil {
   385  			k.G().Log.Warning("SKBKeyringFile: WriteTo b64.Close() error: %s", closeErr)
   386  			if err == nil {
   387  				n = 0
   388  				err = closeErr
   389  				return
   390  			}
   391  		}
   392  		k.G().Log.Debug("SKBKeyringFile: b64 stream closed successfully")
   393  	}()
   394  
   395  	if err := encodeSKBPacketList(k.Blocks, b64); err != nil {
   396  		k.G().Log.Warning("Encoding problem: %s", err)
   397  		return 0, err
   398  	}
   399  
   400  	return 0, nil
   401  }
   402  
   403  func (k *SKBKeyringFile) Bug3964Repair(m MetaContext, lks *LKSec, dkm DeviceKeyMap) (ret *SKBKeyringFile, serverHalfSet *LKSecServerHalfSet, err error) {
   404  	defer m.Trace("SKBKeyringFile#Bug3964Repair", &err)()
   405  
   406  	var newBlocks []*SKB
   407  	var hitBug3964 bool
   408  
   409  	m.Debug("| # of blocks=%d", len(k.Blocks))
   410  
   411  	for i, b := range k.Blocks {
   412  
   413  		if b.Priv.Data == nil {
   414  			m.Debug("| Null private data at block=%d", i)
   415  			newBlocks = append(newBlocks, b)
   416  			continue
   417  		}
   418  
   419  		if b.Priv.Encryption != LKSecVersion {
   420  			m.Debug("| Skipping non-LKSec encryption (%d) at block=%d", b.Priv.Encryption, i)
   421  			newBlocks = append(newBlocks, b)
   422  			continue
   423  		}
   424  
   425  		var decryption, reencryption []byte
   426  		var badMask LKSecServerHalf
   427  		decryption, badMask, err = lks.decryptForBug3964Repair(m, b.Priv.Data, dkm)
   428  		if err != nil {
   429  			m.Debug("| Decryption failed at block=%d; keeping as is (%s)", i, err)
   430  			newBlocks = append(newBlocks, b)
   431  			continue
   432  		}
   433  
   434  		if badMask.IsNil() {
   435  			m.Debug("| Nil badmask at block=%d", i)
   436  			newBlocks = append(newBlocks, b)
   437  			continue
   438  		}
   439  
   440  		hitBug3964 = true
   441  		m.Debug("| Hit bug 3964 at SKB block=%d", i)
   442  		if serverHalfSet == nil {
   443  			serverHalfSet = NewLKSecServerHalfSet()
   444  		}
   445  		serverHalfSet.Add(badMask)
   446  
   447  		reencryption, err = lks.Encrypt(m, decryption)
   448  		if err != nil {
   449  			m.Debug("| reencryption bug at block=%d", i)
   450  			return nil, nil, err
   451  		}
   452  
   453  		newSKB := &SKB{
   454  			Contextified: NewContextified(m.G()),
   455  			Pub:          b.Pub,
   456  			Type:         b.Type,
   457  			Priv: SKBPriv{
   458  				Encryption:           b.Priv.Encryption,
   459  				PassphraseGeneration: b.Priv.PassphraseGeneration,
   460  				Data:                 reencryption,
   461  			},
   462  		}
   463  
   464  		newBlocks = append(newBlocks, newSKB)
   465  	}
   466  	if !hitBug3964 {
   467  		return nil, nil, nil
   468  	}
   469  
   470  	ret = NewSKBKeyringFile(k.G(), k.username)
   471  	ret.dirty = true
   472  	ret.Blocks = newBlocks
   473  
   474  	err = ret.Index()
   475  	if err != nil {
   476  		return nil, nil, err
   477  	}
   478  
   479  	return ret, serverHalfSet, nil
   480  }