github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/sig.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  	"encoding/hex"
     9  	"fmt"
    10  	"io"
    11  
    12  	"strings"
    13  
    14  	"github.com/keybase/client/go/kbcrypto"
    15  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    16  	"github.com/keybase/go-crypto/openpgp"
    17  	"github.com/keybase/go-crypto/openpgp/armor"
    18  	jsonw "github.com/keybase/go-jsonw"
    19  )
    20  
    21  func GetSigID(w *jsonw.Wrapper) (keybase1.SigID, error) {
    22  	s, err := w.GetString()
    23  	if err != nil {
    24  		return "", err
    25  	}
    26  	return keybase1.SigIDFromString(s)
    27  }
    28  
    29  func GetSigIDBase(w *jsonw.Wrapper) (keybase1.SigIDBase, error) {
    30  	s, err := w.GetString()
    31  	if err != nil {
    32  		return "", err
    33  	}
    34  	return keybase1.SigIDBaseFromString(s)
    35  }
    36  
    37  type ParsedSig struct {
    38  	Block       *armor.Block
    39  	SigBody     []byte
    40  	MD          *openpgp.MessageDetails
    41  	LiteralData []byte
    42  }
    43  
    44  func PGPOpenSig(armored string) (ps *ParsedSig, err error) {
    45  	pso := ParsedSig{}
    46  	pso.Block, err = armor.Decode(strings.NewReader(cleanPGPInput(armored)))
    47  	if err != nil {
    48  		return
    49  	}
    50  	pso.SigBody, err = io.ReadAll(pso.Block.Body)
    51  	if err != nil {
    52  		return
    53  	}
    54  	ps = &pso
    55  	return
    56  }
    57  
    58  // OpenSig takes an armored PGP or Keybase signature and opens
    59  // the armor.  It will return the body of the signature, the
    60  // sigID of the body, or an error if it didn't work out.
    61  func OpenSig(armored string) (ret []byte, id keybase1.SigIDBase, err error) {
    62  	if isPGPBundle(armored) {
    63  		var ps *ParsedSig
    64  		if ps, err = PGPOpenSig(armored); err == nil {
    65  			ret = ps.SigBody
    66  			id = ps.ID()
    67  		}
    68  	} else {
    69  		if ret, err = KbOpenSig(armored); err == nil {
    70  			id = kbcrypto.ComputeSigIDFromSigBody(ret)
    71  		}
    72  	}
    73  	return
    74  }
    75  
    76  // SigExtractPayloadAndKID extracts the payload and KID of the key that
    77  // was supposedly used to sign this message. A KID will only be returned
    78  // for KB messages, and not for PGP messages
    79  func SigExtractPayloadAndKID(armored string) (payload []byte, kid keybase1.KID, sigID keybase1.SigIDBase, err error) {
    80  	if isPGPBundle(armored) {
    81  		payload, sigID, err = SigExtractPGPPayload(armored)
    82  	} else {
    83  		payload, kid, sigID, err = SigExtractKbPayloadAndKID(armored)
    84  	}
    85  	return payload, kid, sigID, err
    86  }
    87  
    88  func SigAssertPayload(armored string, expected []byte) (sigID keybase1.SigIDBase, err error) {
    89  	if isPGPBundle(armored) {
    90  		return SigAssertPGPPayload(armored, expected)
    91  	}
    92  	return SigAssertKbPayload(armored, expected)
    93  }
    94  
    95  func SigAssertPGPPayload(armored string, expected []byte) (sigID keybase1.SigIDBase, err error) {
    96  	var ps *ParsedSig
    97  	ps, err = PGPOpenSig(armored)
    98  	if err != nil {
    99  		return
   100  	}
   101  	if err = ps.AssertPayload(expected); err != nil {
   102  		ps = nil
   103  		return
   104  	}
   105  	sigID = ps.ID()
   106  	return
   107  }
   108  
   109  func SigExtractPGPPayload(armored string) (payload []byte, sigID keybase1.SigIDBase, err error) {
   110  	var ps *ParsedSig
   111  	ps, err = PGPOpenSig(armored)
   112  	if err != nil {
   113  		return nil, sigID, err
   114  	}
   115  	payload, err = ps.ExtractPayload()
   116  	if err != nil {
   117  		return nil, sigID, err
   118  	}
   119  	return payload, ps.ID(), nil
   120  }
   121  
   122  func (ps *ParsedSig) ExtractPayload() (payload []byte, err error) {
   123  
   124  	ring := EmptyKeyRing{}
   125  	md, err := openpgp.ReadMessage(bytes.NewReader(ps.SigBody), ring, nil, nil)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	data, err := io.ReadAll(md.UnverifiedBody)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	return data, nil
   134  }
   135  
   136  func (ps *ParsedSig) AssertPayload(expected []byte) error {
   137  
   138  	data, err := ps.ExtractPayload()
   139  	if err != nil {
   140  		return err
   141  	}
   142  
   143  	if !FastByteArrayEq(data, expected) {
   144  		err = fmt.Errorf("Signature did not contain expected text")
   145  		return err
   146  	}
   147  	return nil
   148  }
   149  
   150  func (ps *ParsedSig) Verify(k PGPKeyBundle) (err error) {
   151  	ps.MD, err = openpgp.ReadMessage(bytes.NewReader(ps.SigBody), k, nil, nil)
   152  	if err != nil {
   153  		return
   154  	}
   155  	if !ps.MD.IsSigned || ps.MD.SignedBy == nil {
   156  		err = fmt.Errorf("Message wasn't signed")
   157  		return
   158  	}
   159  	if !k.MatchesKey(ps.MD.SignedBy) {
   160  		err = fmt.Errorf("Got wrong SignedBy key %v",
   161  			hex.EncodeToString(ps.MD.SignedBy.PublicKey.Fingerprint[:]))
   162  		return
   163  	}
   164  	if ps.MD.UnverifiedBody == nil {
   165  		err = fmt.Errorf("no signed material found")
   166  		return
   167  	}
   168  
   169  	ps.LiteralData, err = io.ReadAll(ps.MD.UnverifiedBody)
   170  	if err != nil {
   171  		return
   172  	}
   173  
   174  	// We'll see a sig error here after reading in the UnverifiedBody above,
   175  	// if there was one to see.
   176  	if err = ps.MD.SignatureError; err != nil {
   177  		return
   178  	}
   179  
   180  	if ps.MD.Signature == nil && ps.MD.SignatureV3 == nil {
   181  		err = fmt.Errorf("No available signature after checking signature")
   182  		return
   183  	}
   184  
   185  	// Hopefully by here we've covered all of our bases.
   186  	return nil
   187  }
   188  
   189  func (ps *ParsedSig) ID() keybase1.SigIDBase {
   190  	return kbcrypto.ComputeSigIDFromSigBody(ps.SigBody)
   191  }
   192  
   193  func IsPGPSig(s string) bool {
   194  	return strings.HasPrefix(s, "-----BEGIN PGP MESSAGE-----")
   195  }