github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/saltpack_decrypt.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  	"bytes"
     8  	"io"
     9  
    10  	"github.com/keybase/client/go/kbcrypto"
    11  	"github.com/keybase/client/go/libkb"
    12  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    13  	"github.com/keybase/saltpack"
    14  	saltpackBasic "github.com/keybase/saltpack/basic"
    15  )
    16  
    17  type SaltpackDecryptArg struct {
    18  	Source io.Reader
    19  	Sink   io.WriteCloser
    20  	Opts   keybase1.SaltpackDecryptOptions
    21  }
    22  
    23  // SaltpackDecrypt decrypts data read from a source into a sink.
    24  type SaltpackDecrypt struct {
    25  	arg          *SaltpackDecryptArg
    26  	res          keybase1.SaltpackEncryptedMessageInfo
    27  	pnymResolver saltpack.SymmetricKeyResolver
    28  }
    29  
    30  // NewSaltpackDecrypt creates a SaltpackDecrypt engine.
    31  func NewSaltpackDecrypt(arg *SaltpackDecryptArg, pnymResolver saltpack.SymmetricKeyResolver) *SaltpackDecrypt {
    32  	return &SaltpackDecrypt{
    33  		arg:          arg,
    34  		pnymResolver: pnymResolver,
    35  	}
    36  }
    37  
    38  // Name is the unique engine name.
    39  func (e *SaltpackDecrypt) Name() string {
    40  	return "SaltpackDecrypt"
    41  }
    42  
    43  // GetPrereqs returns the engine prereqs.
    44  func (e *SaltpackDecrypt) Prereqs() Prereqs {
    45  	return Prereqs{}
    46  }
    47  
    48  // RequiredUIs returns the required UIs.
    49  func (e *SaltpackDecrypt) RequiredUIs() []libkb.UIKind {
    50  	return []libkb.UIKind{
    51  		libkb.SaltpackUIKind,
    52  		libkb.SecretUIKind,
    53  	}
    54  }
    55  
    56  // SubConsumers returns the other UI consumers for this engine.
    57  func (e *SaltpackDecrypt) SubConsumers() []libkb.UIConsumer {
    58  	return []libkb.UIConsumer{
    59  		&SaltpackSenderIdentify{},
    60  	}
    61  }
    62  
    63  func (e *SaltpackDecrypt) promptForDecrypt(m libkb.MetaContext, publicKey keybase1.KID, isAnon, signed bool) (err error) {
    64  	defer m.Trace("SaltpackDecrypt#promptForDecrypt", &err)()
    65  
    66  	spsiArg := SaltpackSenderIdentifyArg{
    67  		isAnon:           isAnon,
    68  		publicKey:        publicKey,
    69  		interactive:      e.arg.Opts.Interactive,
    70  		forceRemoteCheck: e.arg.Opts.ForceRemoteCheck,
    71  		reason: keybase1.IdentifyReason{
    72  			Reason: "Identify who encrypted this message",
    73  			Type:   keybase1.IdentifyReasonType_DECRYPT,
    74  		},
    75  	}
    76  
    77  	spsiEng := NewSaltpackSenderIdentify(m.G(), &spsiArg)
    78  	if err = RunEngine2(m, spsiEng); err != nil {
    79  		return err
    80  	}
    81  
    82  	arg := keybase1.SaltpackPromptForDecryptArg{
    83  		Sender:     spsiEng.Result(),
    84  		SigningKID: publicKey,
    85  		Signed:     signed,
    86  	}
    87  	e.res.Sender = arg.Sender
    88  
    89  	usedDelegateUI := false
    90  	if m.G().UIRouter != nil {
    91  		if ui, err := m.G().UIRouter.GetIdentifyUI(); err == nil && ui != nil {
    92  			usedDelegateUI = true
    93  		}
    94  	}
    95  
    96  	err = m.UIs().SaltpackUI.SaltpackPromptForDecrypt(m.Ctx(), arg, usedDelegateUI)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	return err
   101  }
   102  
   103  func (e *SaltpackDecrypt) makeMessageInfo(me *libkb.User, mki *saltpack.MessageKeyInfo) {
   104  	if mki == nil || me == nil {
   105  		return
   106  	}
   107  	ckf := me.GetComputedKeyFamily()
   108  	for _, nr := range mki.NamedReceivers {
   109  		kid := keybase1.KIDFromRawKey(nr, byte(kbcrypto.KIDNaclDH))
   110  		if dev, _ := ckf.GetDeviceForKID(kid); dev != nil {
   111  			edev := dev.ProtExport()
   112  			edev.EncryptKey = kid
   113  			e.res.Devices = append(e.res.Devices, *edev)
   114  		}
   115  	}
   116  	e.res.NumAnonReceivers = mki.NumAnonReceivers
   117  	e.res.ReceiverIsAnon = mki.ReceiverIsAnon
   118  }
   119  
   120  func addToKeyring(keyring *saltpackBasic.Keyring, key *libkb.NaclDHKeyPair) {
   121  	keyring.ImportBoxKey((*[libkb.NaclDHKeysize]byte)(&key.Public), (*[libkb.NaclDHKeysize]byte)(key.Private))
   122  }
   123  
   124  // Used when decrypting with a paper key, as in that case there is no active device/user and we cannot rely on the
   125  // pseudonym mechanism.
   126  type nilPseudonymResolver struct{}
   127  
   128  func (t *nilPseudonymResolver) ResolveKeys(identifiers [][]byte) ([]*saltpack.SymmetricKey, error) {
   129  	return make([]*saltpack.SymmetricKey, len(identifiers)), nil
   130  }
   131  
   132  // Run starts the engine.
   133  func (e *SaltpackDecrypt) Run(m libkb.MetaContext) (err error) {
   134  	defer m.Trace("SaltpackDecrypt::Run", &err)()
   135  
   136  	// We don't load this in the --paperkey case.
   137  	var me *libkb.User
   138  
   139  	keyring := saltpackBasic.NewKeyring()
   140  
   141  	if e.arg.Opts.UsePaperKey {
   142  		// Prompt the user for a paper key. This doesn't require you to be
   143  		// logged in.
   144  		keypair, _, err := getPaperKey(m, nil, nil)
   145  		if err != nil {
   146  			return err
   147  		}
   148  		encryptionNaclKeyPair := keypair.EncryptionKey().(libkb.NaclDHKeyPair)
   149  		addToKeyring(keyring, &encryptionNaclKeyPair)
   150  
   151  		// If a paper key is used, we do not have PUK or an active session, so we cannot talk to the server to resolve pseudonym.
   152  		m.Debug("substituting the default PseudonymResolver as a paper key is being used for decryption")
   153  		e.pnymResolver = &nilPseudonymResolver{}
   154  	} else {
   155  		// This does require you to be logged in.
   156  		if !m.G().ActiveDevice.HaveKeys() {
   157  			return libkb.LoginRequiredError{}
   158  		}
   159  
   160  		// Only used in the makeMessageInfo call, which is helpful for old messages (one cannot encrypt messages with visible recipients any more).
   161  		me, err = libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m))
   162  		if err != nil {
   163  			return err
   164  		}
   165  
   166  		// Get the device encryption key and per user keys.
   167  		var key *libkb.NaclDHKeyPair
   168  		var err error
   169  		key, err = m.G().ActiveDevice.NaclEncryptionKey()
   170  		if err != nil {
   171  			return err
   172  		}
   173  		m.Debug("adding device key for decryption: %v", key.GetKID())
   174  		addToKeyring(keyring, key)
   175  
   176  		perUserKeyring, err := m.G().GetPerUserKeyring(m.Ctx())
   177  		if err != nil {
   178  			return err
   179  		}
   180  		pukGen := perUserKeyring.CurrentGeneration()
   181  		for i := 1; i <= int(pukGen); i++ {
   182  			key, err = perUserKeyring.GetEncryptionKeyByGeneration(m, keybase1.PerUserKeyGeneration(i))
   183  			m.Debug("adding per user key at generation %v for decryption: %v", i, key.GetKID())
   184  			if err != nil {
   185  				return err
   186  			}
   187  			addToKeyring(keyring, key)
   188  		}
   189  	}
   190  
   191  	// For DH mode.
   192  	hookMki := func(mki *saltpack.MessageKeyInfo) error {
   193  		kidToIdentify := libkb.BoxPublicKeyToKeybaseKID(mki.SenderKey)
   194  		return e.promptForDecrypt(m, kidToIdentify, mki.SenderIsAnon, false /* not signed */)
   195  	}
   196  
   197  	// For signcryption mode.
   198  	hookSenderSigningKey := func(senderSigningKey saltpack.SigningPublicKey) error {
   199  		kidToIdentify := libkb.SigningPublicKeyToKeybaseKID(senderSigningKey)
   200  		// See if the sender signing key is nil or all zeroes.
   201  		isAnon := false
   202  		signed := true
   203  		if senderSigningKey == nil || bytes.Equal(senderSigningKey.ToKID(), make([]byte, len(senderSigningKey.ToKID()))) {
   204  			isAnon = true
   205  			signed = false
   206  		}
   207  		return e.promptForDecrypt(m, kidToIdentify, isAnon, signed)
   208  	}
   209  
   210  	m.Debug("| SaltpackDecrypt")
   211  	var mki *saltpack.MessageKeyInfo
   212  	mki, err = libkb.SaltpackDecrypt(m, e.arg.Source, e.arg.Sink, keyring, hookMki, hookSenderSigningKey, e.pnymResolver)
   213  
   214  	if decErr, ok := err.(libkb.DecryptionError); ok && decErr.Cause.Err == saltpack.ErrNoDecryptionKey {
   215  		m.Debug("switching cause of libkb.DecryptionError from saltpack.ErrNoDecryptionKey to more specific libkb.NoDecryptionKeyError")
   216  		if e.arg.Opts.UsePaperKey {
   217  			return libkb.DecryptionError{Cause: libkb.ErrorCause{Err: libkb.NoDecryptionKeyError{Msg: "this message was not directly encrypted for the given paper key. In some cases, you might still be able to decrypt the message from a device provisioned with this key."}, StatusCode: libkb.SCDecryptionKeyNotFound}}
   218  		}
   219  		err = libkb.DecryptionError{Cause: libkb.ErrorCause{Err: libkb.NoDecryptionKeyError{Msg: "no suitable key found"}, StatusCode: libkb.SCDecryptionKeyNotFound}}
   220  	}
   221  
   222  	// Since messages recipients are never public any more, this is only meaningful for messages generated by
   223  	// very old clients (or potentially saltpack messages generated for a keybase user by some other app).
   224  	// It's ok if me is nil here.
   225  	e.makeMessageInfo(me, mki)
   226  
   227  	return err
   228  }
   229  
   230  func (e *SaltpackDecrypt) MessageInfo() keybase1.SaltpackEncryptedMessageInfo {
   231  	return e.res
   232  }