github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/common.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 engine
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/keybase/client/go/libkb"
    10  )
    11  
    12  // findPaperKeys checks if the user has paper backup keys.  If he/she
    13  // does, it prompts for a paperkey phrase.  This is used to
    14  // regenerate paper keys, which are then matched against the
    15  // paper keys found in the keyfamily.
    16  func findPaperKeys(m libkb.MetaContext, me *libkb.User) (*libkb.DeviceWithKeys, error) {
    17  	cki := me.GetComputedKeyInfos()
    18  	if cki == nil {
    19  		return nil, fmt.Errorf("no computed key infos")
    20  	}
    21  	bdevs := cki.PaperDevices()
    22  	if len(bdevs) == 0 {
    23  		return nil, libkb.NoPaperKeysError{}
    24  	}
    25  
    26  	passphrase, err := libkb.GetPaperKeyPassphrase(m, m.UIs().SecretUI, me.GetName(), nil, nil)
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  
    31  	return matchPaperKey(m, me, passphrase)
    32  }
    33  
    34  // matchPaperKey checks to make sure paper is a valid paper phrase and that it exists
    35  // in the user's keyfamily.
    36  func matchPaperKey(m libkb.MetaContext, me *libkb.User, paper string) (*libkb.DeviceWithKeys, error) {
    37  	cki := me.GetComputedKeyInfos()
    38  	if cki == nil {
    39  		return nil, fmt.Errorf("no computed key infos")
    40  	}
    41  	bdevs := cki.PaperDevices()
    42  	if len(bdevs) == 0 {
    43  		return nil, libkb.NoPaperKeysError{}
    44  	}
    45  
    46  	pc := new(libkb.PaperChecker)
    47  	if err := pc.Check(m, paper); err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	// phrase has the correct version and contains valid words
    52  
    53  	paperPhrase := libkb.NewPaperKeyPhrase(paper)
    54  
    55  	bkarg := &PaperKeyGenArg{
    56  		Passphrase: paperPhrase,
    57  		SkipPush:   true,
    58  		Me:         me,
    59  	}
    60  	bkeng := NewPaperKeyGen(m.G(), bkarg)
    61  	if err := RunEngine2(m, bkeng); err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	sigKey := bkeng.SigKey()
    66  	encKey := bkeng.EncKey()
    67  	var device *libkb.Device
    68  
    69  	m.Debug("generated paper key signing kid: %s", sigKey.GetKID())
    70  	m.Debug("generated paper key encryption kid: %s", encKey.GetKID())
    71  
    72  	ckf := me.GetComputedKeyFamily()
    73  	for _, bdev := range bdevs {
    74  		sk, err := ckf.GetSibkeyForDevice(bdev.ID)
    75  		if err != nil {
    76  			m.Debug("ckf.GetSibkeyForDevice(%s) error: %s", bdev.ID, err)
    77  			continue
    78  		}
    79  		m.Debug("paper key device %s signing kid: %s", bdev.ID, sk.GetKID())
    80  		ek, err := ckf.GetEncryptionSubkeyForDevice(bdev.ID)
    81  		if err != nil {
    82  			m.Debug("ckf.GetEncryptionSubkeyForDevice(%s) error: %s", bdev.ID, err)
    83  			continue
    84  		}
    85  		m.Debug("paper key device %s encryption kid: %s", bdev.ID, ek.GetKID())
    86  
    87  		if sk.GetKID().Equal(sigKey.GetKID()) && ek.GetKID().Equal(encKey.GetKID()) {
    88  			m.Debug("paper key device %s matches generated paper key", bdev.ID)
    89  			device = bdev
    90  			break
    91  		}
    92  
    93  		m.Debug("paper key device %s does not match generated paper key", bdev.ID)
    94  	}
    95  
    96  	if device == nil {
    97  		m.Debug("no matching paper keys found")
    98  		return nil, libkb.PassphraseError{Msg: "no matching paper backup keys found"}
    99  	}
   100  
   101  	var deviceName string
   102  	if device.Description != nil {
   103  		deviceName = *device.Description
   104  	}
   105  	return libkb.NewDeviceWithKeys(sigKey, encKey, device.ID, deviceName, libkb.KeychainModeNone), nil
   106  }
   107  
   108  // fetchLKS gets the encrypted LKS client half from the server.
   109  // It uses encKey to decrypt it.  It also returns the passphrase
   110  // generation.
   111  func fetchLKS(m libkb.MetaContext, encKey libkb.GenericKey) (libkb.PassphraseGeneration, libkb.LKSecClientHalf, error) {
   112  	arg := libkb.APIArg{
   113  		Endpoint:    "passphrase/recover",
   114  		SessionType: libkb.APISessionTypeREQUIRED,
   115  		Args: libkb.HTTPArgs{
   116  			"kid": encKey.GetKID(),
   117  		},
   118  	}
   119  	res, err := m.G().API.Get(m, arg)
   120  	var dummy libkb.LKSecClientHalf
   121  	if err != nil {
   122  		return 0, dummy, err
   123  	}
   124  	ctext, err := res.Body.AtKey("ctext").GetString()
   125  	if err != nil {
   126  		return 0, dummy, err
   127  	}
   128  	ppGen, err := res.Body.AtKey("passphrase_generation").GetInt()
   129  	if err != nil {
   130  		return 0, dummy, err
   131  	}
   132  
   133  	// Now try to decrypt with the unlocked device key
   134  	msg, _, err := encKey.DecryptFromString(ctext)
   135  	if err != nil {
   136  		return 0, dummy, err
   137  	}
   138  	clientHalf, err := libkb.NewLKSecClientHalfFromBytes(msg)
   139  	if err != nil {
   140  		return 0, dummy, err
   141  	}
   142  
   143  	return libkb.PassphraseGeneration(ppGen), clientHalf, nil
   144  }