github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbcrypto/nacl_sig_info.go (about)

     1  // Copyright 2018 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package kbcrypto
     5  
     6  import (
     7  	"encoding/base64"
     8  	"errors"
     9  	"fmt"
    10  
    11  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    12  )
    13  
    14  type NaclSigInfo struct {
    15  	Kid      keybase1.BinaryKID `codec:"key"`
    16  	Payload  []byte             `codec:"payload,omitempty"`
    17  	Sig      NaclSignature      `codec:"sig"`
    18  	SigType  AlgoType           `codec:"sig_type"`
    19  	HashType HashType           `codec:"hash_type"`
    20  	Detached bool               `codec:"detached"`
    21  	Version  int                `codec:"version,omitempty"`
    22  	Prefix   SignaturePrefix    `codec:"prefix,omitempty"`
    23  }
    24  
    25  func (s *NaclSigInfo) GetTagAndVersion() (PacketTag, PacketVersion) {
    26  	return TagSignature, KeybasePacketV1
    27  }
    28  
    29  var _ Packetable = (*NaclSigInfo)(nil)
    30  
    31  type BadKeyError struct {
    32  	Msg string
    33  }
    34  
    35  func (p BadKeyError) Error() string {
    36  	msg := "Bad key found"
    37  	if len(p.Msg) != 0 {
    38  		msg = msg + ": " + p.Msg
    39  	}
    40  	return msg
    41  }
    42  
    43  type VerificationError struct {
    44  	// XXX - NOTE(maxtaco) - 20190418 - this is not to be confused with Cause(), which interacts with the pkg/errors
    45  	// system. There should probably be a better solution than this, but let's leave it for now.
    46  	Cause error
    47  }
    48  
    49  func newVerificationErrorWithString(s string) VerificationError {
    50  	return VerificationError{Cause: errors.New(s)}
    51  }
    52  
    53  func NewVerificationError(e error) VerificationError {
    54  	return VerificationError{Cause: e}
    55  }
    56  
    57  func (e VerificationError) Error() string {
    58  	if e.Cause == nil {
    59  		return "Verification failed"
    60  	}
    61  	return fmt.Sprintf("Verification failed: %s", e.Cause.Error())
    62  }
    63  
    64  func (e VerificationError) ToStatus() keybase1.Status {
    65  	cause := ""
    66  	if e.Cause != nil {
    67  		cause = e.Cause.Error()
    68  	}
    69  	return keybase1.Status{
    70  		Code: SCSigCannotVerify,
    71  		Name: "SC_SIG_CANNOT_VERIFY",
    72  		Fields: []keybase1.StringKVPair{
    73  			{Key: "Cause", Value: cause},
    74  		},
    75  	}
    76  }
    77  
    78  type UnhandledSignatureError struct {
    79  	version int
    80  }
    81  
    82  func (e UnhandledSignatureError) Error() string {
    83  	return fmt.Sprintf("unhandled signature version: %d", e.version)
    84  }
    85  
    86  func (s NaclSigInfo) Verify() (*NaclSigningKeyPublic, error) {
    87  	return s.verifyWithPayload(s.Payload, false)
    88  }
    89  
    90  // verifyWithPayload verifies the NaclSigInfo s, with the payload payload. Note that
    91  // s may or may not have a payload already baked into it. If it does, and checkPayloadEquality
    92  // is true, then we assert that the "baked-in" payload is equal to the specified payload.
    93  // We'll only pass `false` for this flag from just above, to avoid checking that s.Payload == s.Payload,
    94  // which we know it does. We need this unfortunate complexity because some signatures the client
    95  // tries to verify are "attached," meaning the payload comes along with the sig info. And in other
    96  // cases, the signatures are "detached", meaning they are supplied out-of-band. This function
    97  // handles both cases.
    98  func (s NaclSigInfo) verifyWithPayload(payload []byte, checkPayloadEquality bool) (*NaclSigningKeyPublic, error) {
    99  	key := KIDToNaclSigningKeyPublic(s.Kid)
   100  	if key == nil {
   101  		return nil, BadKeyError{}
   102  	}
   103  	if payload == nil {
   104  		return nil, newVerificationErrorWithString("nil payload")
   105  	}
   106  	if len(payload) == 0 {
   107  		return nil, newVerificationErrorWithString("empty payload")
   108  	}
   109  
   110  	if checkPayloadEquality && s.Payload != nil && !SecureByteArrayEq(payload, s.Payload) {
   111  		return nil, newVerificationErrorWithString("payload mismatch")
   112  	}
   113  
   114  	switch s.Version {
   115  	case 0, 1:
   116  		if !key.Verify(payload, s.Sig) {
   117  			return nil, newVerificationErrorWithString("verify failed")
   118  		}
   119  	case 2:
   120  		if !s.Prefix.IsWhitelisted() {
   121  			return nil, newVerificationErrorWithString("unknown prefix")
   122  		}
   123  		if !key.Verify(s.Prefix.Prefix(payload), s.Sig) {
   124  			return nil, newVerificationErrorWithString("verify failed")
   125  		}
   126  	default:
   127  		return nil, UnhandledSignatureError{s.Version}
   128  	}
   129  
   130  	return key, nil
   131  }
   132  
   133  func (s *NaclSigInfo) ArmoredEncode() (ret string, err error) {
   134  	return EncodePacketToArmoredString(s)
   135  }
   136  
   137  func DecodeNaclSigInfoPacket(data []byte) (NaclSigInfo, error) {
   138  	var info NaclSigInfo
   139  	err := DecodePacketFromBytes(data, &info)
   140  	if err != nil {
   141  		return NaclSigInfo{}, err
   142  	}
   143  	return info, nil
   144  }
   145  
   146  func DecodeArmoredNaclSigInfoPacket(s string) (NaclSigInfo, error) {
   147  	b, err := base64.StdEncoding.DecodeString(s)
   148  	if err != nil {
   149  		return NaclSigInfo{}, err
   150  	}
   151  	return DecodeNaclSigInfoPacket(b)
   152  }
   153  
   154  // NaclVerifyAndExtract interprets the given string as a NaCl-signed messaged, in
   155  // the keybase NaclSigInfo (v1) format. It will check that the signature verified, and if so,
   156  // will return the public key that was used for the verification, the payload of the signature,
   157  // the full body of the decoded SignInfo, and an error
   158  func NaclVerifyAndExtract(s string) (nk *NaclSigningKeyPublic, payload []byte, fullBody []byte, err error) {
   159  	fullBody, err = base64.StdEncoding.DecodeString(s)
   160  	if err != nil {
   161  		return nil, nil, nil, err
   162  	}
   163  
   164  	naclSig, err := DecodeNaclSigInfoPacket(fullBody)
   165  	if err != nil {
   166  		return nil, nil, nil, err
   167  	}
   168  
   169  	nk, err = naclSig.Verify()
   170  	if err != nil {
   171  		return nil, nil, nil, err
   172  	}
   173  
   174  	payload = naclSig.Payload
   175  	return nk, payload, fullBody, nil
   176  }
   177  
   178  func NaclVerifyWithPayload(sig string, payloadIn []byte) (nk *NaclSigningKeyPublic, fullBody []byte, err error) {
   179  	fullBody, err = base64.StdEncoding.DecodeString(sig)
   180  	if err != nil {
   181  		return nil, nil, err
   182  	}
   183  
   184  	naclSig, err := DecodeNaclSigInfoPacket(fullBody)
   185  	if err != nil {
   186  		return nil, nil, err
   187  	}
   188  
   189  	nk, err = naclSig.verifyWithPayload(payloadIn, true)
   190  	if err != nil {
   191  		return nil, nil, err
   192  	}
   193  
   194  	return nk, fullBody, nil
   195  }
   196  
   197  func (s NaclSigInfo) SigID() (ret keybase1.SigIDBase, err error) {
   198  	var body []byte
   199  	body, err = EncodePacketToBytes(&s)
   200  	if err != nil {
   201  		return "", err
   202  	}
   203  	return ComputeSigIDFromSigBody(body), nil
   204  }