github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/saltpack_encrypt.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  	"io"
     9  
    10  	"github.com/keybase/client/go/libkb"
    11  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    12  	"github.com/keybase/saltpack"
    13  )
    14  
    15  type SaltpackEncryptArg struct {
    16  	Opts   keybase1.SaltpackEncryptOptions
    17  	Source io.Reader
    18  	Sink   io.WriteCloser
    19  }
    20  
    21  // SaltpackEncrypt encrypts data read from a source into a sink
    22  // for a set of users.  It will track them if necessary.
    23  type SaltpackEncrypt struct {
    24  	arg *SaltpackEncryptArg
    25  	me  keybase1.UID
    26  
    27  	newKeyfinderHook (func(arg libkb.SaltpackRecipientKeyfinderArg) libkb.SaltpackRecipientKeyfinderEngineInterface)
    28  
    29  	// keep track if an SBS recipient was used so callers can tell the user
    30  	UsedSBS      bool
    31  	SBSAssertion string
    32  
    33  	// Legacy encryption-only messages include a lot more information about
    34  	// receivers, and it's nice to keep the helpful errors working while those
    35  	// messages are still around.
    36  	visibleRecipientsForTesting bool
    37  }
    38  
    39  // NewSaltpackEncrypt creates a SaltpackEncrypt engine.
    40  func NewSaltpackEncrypt(arg *SaltpackEncryptArg, newKeyfinderHook func(arg libkb.SaltpackRecipientKeyfinderArg) libkb.SaltpackRecipientKeyfinderEngineInterface) *SaltpackEncrypt {
    41  	return &SaltpackEncrypt{
    42  		arg:              arg,
    43  		newKeyfinderHook: newKeyfinderHook,
    44  	}
    45  }
    46  
    47  // Name is the unique engine name.
    48  func (e *SaltpackEncrypt) Name() string {
    49  	return "SaltpackEncrypt"
    50  }
    51  
    52  // Prereqs returns the engine prereqs.
    53  func (e *SaltpackEncrypt) Prereqs() Prereqs {
    54  	return Prereqs{}
    55  }
    56  
    57  // RequiredUIs returns the required UIs.
    58  func (e *SaltpackEncrypt) RequiredUIs() []libkb.UIKind {
    59  	return []libkb.UIKind{
    60  		libkb.SecretUIKind,
    61  	}
    62  }
    63  
    64  // SubConsumers returns the other UI consumers for this engine.
    65  func (e *SaltpackEncrypt) SubConsumers() []libkb.UIConsumer {
    66  	// Note that potentially KeyfinderHook might return a different UIConsumer depending on its arguments,
    67  	// which might make this call problematic, but all the hooks currently in use are not doing that.
    68  	return []libkb.UIConsumer{
    69  		e.newKeyfinderHook(libkb.SaltpackRecipientKeyfinderArg{}),
    70  	}
    71  }
    72  
    73  func (e *SaltpackEncrypt) loadMe(m libkb.MetaContext) error {
    74  	loggedIn, uid, err := isLoggedInWithUIDAndError(m)
    75  	if err != nil && !e.arg.Opts.NoSelfEncrypt {
    76  		return err
    77  	}
    78  	if !loggedIn {
    79  		return nil
    80  	}
    81  	e.me = uid
    82  	return nil
    83  }
    84  
    85  // Run starts the engine.
    86  func (e *SaltpackEncrypt) Run(m libkb.MetaContext) (err error) {
    87  	defer m.Trace("SaltpackEncrypt::Run", &err)()
    88  
    89  	if err = e.loadMe(m); err != nil {
    90  		return err
    91  	}
    92  
    93  	if !(e.arg.Opts.UseEntityKeys || e.arg.Opts.UseDeviceKeys || e.arg.Opts.UsePaperKeys || e.arg.Opts.UseKBFSKeysOnlyForTesting) {
    94  		return fmt.Errorf("no key type for encryption was specified")
    95  	}
    96  
    97  	kfarg := libkb.SaltpackRecipientKeyfinderArg{
    98  		Recipients:        e.arg.Opts.Recipients,
    99  		TeamRecipients:    e.arg.Opts.TeamRecipients,
   100  		NoSelfEncrypt:     e.arg.Opts.NoSelfEncrypt,
   101  		UseEntityKeys:     e.arg.Opts.UseEntityKeys,
   102  		UsePaperKeys:      e.arg.Opts.UsePaperKeys,
   103  		UseDeviceKeys:     e.arg.Opts.UseDeviceKeys,
   104  		UseRepudiableAuth: e.arg.Opts.AuthenticityType == keybase1.AuthenticityType_REPUDIABLE,
   105  		NoForcePoll:       e.arg.Opts.NoForcePoll,
   106  	}
   107  
   108  	kf := e.newKeyfinderHook(kfarg)
   109  	if err := RunEngine2(m, kf); err != nil {
   110  		return err
   111  	}
   112  
   113  	var receivers []libkb.NaclDHKeyPublic
   114  	for _, KID := range kf.GetPublicKIDs() {
   115  		gk, err := libkb.ImportKeypairFromKID(KID)
   116  		if err != nil {
   117  			return err
   118  		}
   119  		kp, ok := gk.(libkb.NaclDHKeyPair)
   120  		if !ok {
   121  			return libkb.KeyCannotEncryptError{}
   122  		}
   123  		receivers = append(receivers, kp.Public)
   124  	}
   125  
   126  	var symmetricReceivers []saltpack.ReceiverSymmetricKey
   127  	for _, key := range kf.GetSymmetricKeys() {
   128  		symmetricReceivers = append(symmetricReceivers, saltpack.ReceiverSymmetricKey{
   129  			Key:        saltpack.SymmetricKey(key.Key),
   130  			Identifier: key.Identifier,
   131  		})
   132  	}
   133  
   134  	e.UsedSBS, e.SBSAssertion = kf.UsedUnresolvedSBSAssertion()
   135  
   136  	if e.UsedSBS {
   137  		actx := m.G().MakeAssertionContext(m)
   138  		expr, err := libkb.AssertionParse(actx, e.SBSAssertion)
   139  		if err == nil {
   140  			social, err := expr.ToSocialAssertion()
   141  			if err == nil && social.Service == "email" {
   142  				// email assertions are pretty ugly, so just return
   143  				// the "User" part for easier handling upstream.
   144  				e.SBSAssertion = social.User
   145  			}
   146  		}
   147  	}
   148  
   149  	// This flag determines whether saltpack is used in signcryption (false)
   150  	// vs encryption (true) format.
   151  	encryptionOnlyMode := false
   152  
   153  	var senderDH libkb.NaclDHKeyPair
   154  	if e.arg.Opts.AuthenticityType == keybase1.AuthenticityType_REPUDIABLE && !e.me.IsNil() {
   155  		encryptionOnlyMode = true
   156  		dhKey, err := m.G().ActiveDevice.EncryptionKeyWithUID(e.me)
   157  		if err != nil {
   158  			return err
   159  		}
   160  		dhKeypair, ok := dhKey.(libkb.NaclDHKeyPair)
   161  		if !ok || dhKeypair.Private == nil {
   162  			return libkb.KeyCannotEncryptError{}
   163  		}
   164  		senderDH = dhKeypair
   165  	}
   166  
   167  	var senderSigning libkb.NaclSigningKeyPair
   168  	if e.arg.Opts.AuthenticityType == keybase1.AuthenticityType_SIGNED && !e.me.IsNil() {
   169  		signingKey, err := m.G().ActiveDevice.SigningKeyWithUID(e.me)
   170  		if err != nil {
   171  			return err
   172  		}
   173  		signingKeypair, ok := signingKey.(libkb.NaclSigningKeyPair)
   174  		if !ok || signingKeypair.Private == nil {
   175  			// Perhaps a KeyCannotEncrypt error, although less accurate, would be more intuitive for the user.
   176  			return libkb.KeyCannotSignError{}
   177  		}
   178  		senderSigning = signingKeypair
   179  	}
   180  
   181  	if e.arg.Opts.AuthenticityType != keybase1.AuthenticityType_ANONYMOUS && e.me.IsNil() {
   182  		return libkb.NewLoginRequiredError("authenticating a message requires login. Either login or use --auth-type=anonymous")
   183  	}
   184  
   185  	saltpackVersion, err := libkb.SaltpackVersionFromArg(e.arg.Opts.SaltpackVersion)
   186  	if err != nil {
   187  		return err
   188  	}
   189  
   190  	encarg := libkb.SaltpackEncryptArg{
   191  		Source:             e.arg.Source,
   192  		Sink:               e.arg.Sink,
   193  		Receivers:          receivers,
   194  		Sender:             senderDH,
   195  		SenderSigning:      senderSigning,
   196  		Binary:             e.arg.Opts.Binary,
   197  		EncryptionOnlyMode: encryptionOnlyMode,
   198  		SymmetricReceivers: symmetricReceivers,
   199  		SaltpackVersion:    saltpackVersion,
   200  
   201  		VisibleRecipientsForTesting: e.visibleRecipientsForTesting,
   202  	}
   203  	return libkb.SaltpackEncrypt(m, &encarg)
   204  }