github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/saltpack_verify.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 libkb
     5  
     6  import (
     7  	"bytes"
     8  	"io"
     9  
    10  	"github.com/keybase/client/go/kbcrypto"
    11  	"github.com/keybase/client/go/logger"
    12  	"github.com/keybase/saltpack"
    13  )
    14  
    15  // SaltpackVerifyContext is context for engine calls
    16  type SaltpackVerifyContext interface {
    17  	GetLog() logger.Logger
    18  }
    19  
    20  // Wraps kbcrypto.Verification error with libkb.VerificationError. You should
    21  // expect a libkb.VerificationError if exposing the error to the GUI.
    22  func getVerificationErrorWithStatusCode(kberr *kbcrypto.VerificationError) (err VerificationError) {
    23  	err.Cause.Err = kberr.Cause
    24  	switch err.Cause.Err.(type) {
    25  	case APINetError:
    26  		err.Cause.StatusCode = SCAPINetworkError
    27  	case saltpack.ErrNoSenderKey:
    28  		err.Cause.StatusCode = SCDecryptionKeyNotFound
    29  	case saltpack.ErrWrongMessageType:
    30  		err.Cause.StatusCode = SCWrongCryptoMsgType
    31  	}
    32  	return err
    33  }
    34  
    35  func SaltpackVerify(g SaltpackVerifyContext, source io.Reader, sink io.WriteCloser, checkSender func(saltpack.SigningPublicKey) error) (err error) {
    36  	defer func() {
    37  		if kbErr, ok := err.(kbcrypto.VerificationError); ok {
    38  			err = getVerificationErrorWithStatusCode(&kbErr)
    39  		}
    40  	}()
    41  
    42  	sc, newSource, err := ClassifyStream(source)
    43  	if err != nil {
    44  		return err
    45  	}
    46  	if sc.Format != CryptoMessageFormatSaltpack {
    47  		return WrongCryptoFormatError{
    48  			Wanted:    CryptoMessageFormatSaltpack,
    49  			Received:  sc.Format,
    50  			Operation: "verify",
    51  		}
    52  	}
    53  
    54  	if sc.Type != CryptoMessageTypeAttachedSignature {
    55  		return kbcrypto.NewVerificationError(
    56  			saltpack.ErrWrongMessageType{
    57  				Wanted:   saltpack.MessageType(CryptoMessageTypeAttachedSignature),
    58  				Received: saltpack.MessageType(sc.Type),
    59  			})
    60  	}
    61  
    62  	source = newSource
    63  	kr := echoKeyring{}
    64  
    65  	var skey saltpack.SigningPublicKey
    66  	var vs io.Reader
    67  	var brand string
    68  	if sc.Armored {
    69  		skey, vs, brand, err = saltpack.NewDearmor62VerifyStream(saltpack.CheckKnownMajorVersion, source, kr)
    70  	} else {
    71  		skey, vs, err = saltpack.NewVerifyStream(saltpack.CheckKnownMajorVersion, source, kr)
    72  	}
    73  	if err != nil {
    74  		g.GetLog().Debug("saltpack.NewDearmor62VerifyStream error: %s", err)
    75  		return kbcrypto.NewVerificationError(err)
    76  	}
    77  
    78  	if checkSender != nil {
    79  		if err = checkSender(skey); err != nil {
    80  			return kbcrypto.NewVerificationError(err)
    81  		}
    82  	}
    83  
    84  	n, err := io.Copy(sink, vs)
    85  	if err != nil {
    86  		return kbcrypto.NewVerificationError(err)
    87  	}
    88  
    89  	if sc.Armored {
    90  		if err = checkSaltpackBrand(brand); err != nil {
    91  			return kbcrypto.NewVerificationError(err)
    92  		}
    93  	}
    94  
    95  	g.GetLog().Debug("Verify: read %d bytes", n)
    96  
    97  	if err := sink.Close(); err != nil {
    98  		return kbcrypto.NewVerificationError(err)
    99  	}
   100  	return nil
   101  }
   102  
   103  func SaltpackVerifyDetached(g SaltpackVerifyContext, message io.Reader, signature []byte, checkSender func(saltpack.SigningPublicKey) error) (err error) {
   104  	defer func() {
   105  		if kbErr, ok := err.(kbcrypto.VerificationError); ok {
   106  			err = getVerificationErrorWithStatusCode(&kbErr)
   107  		}
   108  	}()
   109  
   110  	sc, _, err := ClassifyStream(bytes.NewReader(signature))
   111  	if err != nil {
   112  		return err
   113  	}
   114  	if sc.Format != CryptoMessageFormatSaltpack {
   115  		return WrongCryptoFormatError{
   116  			Wanted:    CryptoMessageFormatSaltpack,
   117  			Received:  sc.Format,
   118  			Operation: "verify detached",
   119  		}
   120  	}
   121  
   122  	kr := echoKeyring{}
   123  
   124  	var skey saltpack.SigningPublicKey
   125  	if sc.Armored {
   126  		var brand string
   127  		skey, brand, err = saltpack.Dearmor62VerifyDetachedReader(saltpack.CheckKnownMajorVersion, message, string(signature), kr)
   128  		if err != nil {
   129  			g.GetLog().Debug("saltpack.Dearmor62VerifyDetachedReader error: %s", err)
   130  			return kbcrypto.NewVerificationError(err)
   131  		}
   132  		if err = checkSaltpackBrand(brand); err != nil {
   133  			return kbcrypto.NewVerificationError(err)
   134  		}
   135  	} else {
   136  		skey, err = saltpack.VerifyDetachedReader(saltpack.CheckKnownMajorVersion, message, signature, kr)
   137  		if err != nil {
   138  			g.GetLog().Debug("saltpack.VerifyDetachedReader error: %s", err)
   139  			return kbcrypto.NewVerificationError(err)
   140  		}
   141  	}
   142  
   143  	if checkSender != nil {
   144  		if err = checkSender(skey); err != nil {
   145  			return kbcrypto.NewVerificationError(err)
   146  		}
   147  	}
   148  
   149  	return nil
   150  }
   151  
   152  type echoKeyring struct {
   153  	Contextified
   154  }
   155  
   156  func (e echoKeyring) LookupSigningPublicKey(kid []byte) saltpack.SigningPublicKey {
   157  	var k kbcrypto.NaclSigningKeyPublic
   158  	copy(k[:], kid)
   159  	return saltSignerPublic{key: k}
   160  }