github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/pgp_sign.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  
    13  	"github.com/keybase/go-crypto/openpgp"
    14  	"github.com/keybase/go-crypto/openpgp/clearsign"
    15  )
    16  
    17  type PGPSignEngine struct {
    18  	arg *PGPSignArg
    19  	libkb.Contextified
    20  
    21  	warnings libkb.HashSecurityWarnings
    22  }
    23  
    24  type PGPSignArg struct {
    25  	Sink   io.WriteCloser
    26  	Source io.ReadCloser
    27  	Opts   keybase1.PGPSignOptions
    28  }
    29  
    30  func (p *PGPSignEngine) Prereqs() Prereqs {
    31  	return Prereqs{
    32  		Device: true,
    33  	}
    34  }
    35  
    36  func (p *PGPSignEngine) Name() string {
    37  	return "PGPSignEngine"
    38  }
    39  
    40  func (p *PGPSignEngine) RequiredUIs() []libkb.UIKind {
    41  	return []libkb.UIKind{
    42  		libkb.SecretUIKind,
    43  		libkb.PgpUIKind,
    44  	}
    45  }
    46  
    47  func (p *PGPSignEngine) SubConsumers() []libkb.UIConsumer {
    48  	return nil
    49  }
    50  
    51  func NewPGPSignEngine(g *libkb.GlobalContext, arg *PGPSignArg) *PGPSignEngine {
    52  	return &PGPSignEngine{
    53  		arg:          arg,
    54  		Contextified: libkb.NewContextified(g),
    55  	}
    56  }
    57  
    58  func (p *PGPSignEngine) Run(m libkb.MetaContext) (err error) {
    59  	var key libkb.GenericKey
    60  	var pgp *libkb.PGPKeyBundle
    61  	var ok bool
    62  	var dumpTo io.WriteCloser
    63  	var written int64
    64  
    65  	defer func() {
    66  		if dumpTo != nil {
    67  			if e := dumpTo.Close(); e != nil {
    68  				p.G().Log.Warning("error closing dumpTo: %s", e)
    69  			}
    70  		}
    71  		if e := p.arg.Sink.Close(); e != nil {
    72  			p.G().Log.Warning("error closing Sink: %s", e)
    73  		}
    74  		if e := p.arg.Source.Close(); e != nil {
    75  			p.G().Log.Warning("error closing Source: %s", e)
    76  		}
    77  	}()
    78  
    79  	me, err := libkb.LoadMe(libkb.NewLoadUserArg(p.G()))
    80  	if err != nil {
    81  		return err
    82  	}
    83  
    84  	ska := libkb.SecretKeyArg{
    85  		Me:       me,
    86  		KeyType:  libkb.PGPKeyType,
    87  		KeyQuery: p.arg.Opts.KeyQuery,
    88  	}
    89  	key, err = p.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(ska, "command-line signature"))
    90  	if err != nil {
    91  		return
    92  	} else if pgp, ok = key.(*libkb.PGPKeyBundle); !ok {
    93  		err = fmt.Errorf("Can only sign with PGP keys")
    94  		return
    95  	}
    96  
    97  	p.warnings = libkb.HashSecurityWarnings{}
    98  	if w := pgp.SecurityWarnings(
    99  		libkb.HashSecurityWarningOurIdentityHash,
   100  	); len(w) > 0 {
   101  		p.warnings = append(p.warnings, w...)
   102  	}
   103  	for _, warning := range p.warnings.Strings() {
   104  		if err := m.UIs().PgpUI.OutputPGPWarning(m.Ctx(), keybase1.OutputPGPWarningArg{
   105  			Warning: warning,
   106  		}); err != nil {
   107  			return err
   108  		}
   109  	}
   110  
   111  	bo := p.arg.Opts.BinaryOut
   112  	bi := p.arg.Opts.BinaryIn
   113  	pgpe := pgp.Entity
   114  	mode := p.arg.Opts.Mode
   115  
   116  	switch mode {
   117  	case keybase1.SignMode_ATTACHED:
   118  		dumpTo, err = libkb.AttachedSignWrapper(p.arg.Sink, *pgp, !bo)
   119  	case keybase1.SignMode_DETACHED:
   120  		switch {
   121  		case bi && bo:
   122  			err = openpgp.DetachSign(p.arg.Sink, pgpe, p.arg.Source, nil)
   123  		case bi && !bo:
   124  			err = openpgp.ArmoredDetachSign(p.arg.Sink, pgpe, p.arg.Source, nil)
   125  		case !bi && bo:
   126  			err = openpgp.DetachSignText(p.arg.Sink, pgpe, p.arg.Source, nil)
   127  		default:
   128  			err = openpgp.ArmoredDetachSignText(p.arg.Sink, pgpe, p.arg.Source, nil)
   129  		}
   130  	case keybase1.SignMode_CLEAR:
   131  		dumpTo, err = clearsign.Encode(p.arg.Sink, pgp.PrivateKey, nil)
   132  	default:
   133  		err = fmt.Errorf("unrecognized sign mode: %d", int(mode))
   134  	}
   135  
   136  	if err != nil {
   137  		return
   138  	}
   139  
   140  	if dumpTo != nil {
   141  		written, err = io.Copy(dumpTo, p.arg.Source)
   142  		if err == nil && written == 0 {
   143  			p.G().Log.Debug("Empty source file.")
   144  		}
   145  	}
   146  	return
   147  }