...

Source file src/github.com/sigstore/rekor/pkg/pki/pgp/pgp.go

Documentation: github.com/sigstore/rekor/pkg/pki/pgp

     1  //
     2  // Copyright 2021 The Sigstore Authors.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package pgp
    17  
    18  import (
    19  	"bufio"
    20  	"bytes"
    21  	"context"
    22  	"crypto/ecdsa"
    23  	"crypto/ed25519"
    24  	"crypto/rsa"
    25  	"encoding/hex"
    26  	"errors"
    27  	"fmt"
    28  	"io"
    29  	"net/http"
    30  
    31  	"github.com/asaskevich/govalidator"
    32  
    33  	//TODO: https://github.com/sigstore/rekor/issues/286
    34  	"golang.org/x/crypto/openpgp"        //nolint:staticcheck
    35  	"golang.org/x/crypto/openpgp/armor"  //nolint:staticcheck
    36  	"golang.org/x/crypto/openpgp/packet" //nolint:staticcheck
    37  
    38  	"github.com/sigstore/rekor/pkg/pki/identity"
    39  	"github.com/sigstore/sigstore/pkg/cryptoutils"
    40  	sigsig "github.com/sigstore/sigstore/pkg/signature"
    41  )
    42  
    43  // Signature that follows the PGP standard; supports both armored & binary detached signatures
    44  type Signature struct {
    45  	isArmored bool
    46  	signature []byte
    47  }
    48  
    49  // NewSignature creates and validates a PGP signature object
    50  func NewSignature(r io.Reader) (*Signature, error) {
    51  	var s Signature
    52  	var inputBuffer bytes.Buffer
    53  
    54  	if _, err := io.Copy(&inputBuffer, r); err != nil {
    55  		return nil, fmt.Errorf("unable to read PGP signature: %w", err)
    56  	}
    57  
    58  	sigByteReader := bytes.NewReader(inputBuffer.Bytes())
    59  
    60  	var sigReader io.Reader
    61  	sigBlock, err := armor.Decode(sigByteReader)
    62  	if err == nil {
    63  		s.isArmored = true
    64  		if sigBlock.Type != openpgp.SignatureType {
    65  			return nil, errors.New("invalid PGP signature provided")
    66  		}
    67  		sigReader = sigBlock.Body
    68  	} else {
    69  		s.isArmored = false
    70  		if _, err := sigByteReader.Seek(0, io.SeekStart); err != nil {
    71  			return nil, fmt.Errorf("unable to read binary PGP signature: %w", err)
    72  		}
    73  		sigReader = sigByteReader
    74  	}
    75  
    76  	sigPktReader := packet.NewReader(sigReader)
    77  	sigPkt, err := sigPktReader.Next()
    78  	if err != nil {
    79  		return nil, fmt.Errorf("invalid PGP signature: %w", err)
    80  	}
    81  
    82  	if _, ok := sigPkt.(*packet.Signature); !ok {
    83  		if _, ok := sigPkt.(*packet.SignatureV3); !ok {
    84  			return nil, errors.New("valid PGP signature was not detected")
    85  		}
    86  	}
    87  
    88  	s.signature = inputBuffer.Bytes()
    89  	return &s, nil
    90  }
    91  
    92  // FetchSignature implements pki.Signature interface
    93  func FetchSignature(ctx context.Context, url string) (*Signature, error) {
    94  	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
    95  	if err != nil {
    96  		return nil, fmt.Errorf("error initializing fetch for PGP signature: %w", err)
    97  	}
    98  	client := &http.Client{}
    99  	resp, err := client.Do(req)
   100  	if err != nil {
   101  		return nil, fmt.Errorf("error fetching PGP signature: %w", err)
   102  	}
   103  	defer resp.Body.Close()
   104  
   105  	sig, err := NewSignature(resp.Body)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	return sig, nil
   110  }
   111  
   112  // CanonicalValue implements the pki.Signature interface
   113  func (s Signature) CanonicalValue() ([]byte, error) {
   114  	if len(s.signature) == 0 {
   115  		return nil, errors.New("PGP signature has not been initialized")
   116  	}
   117  
   118  	if s.isArmored {
   119  		return s.signature, nil
   120  	}
   121  
   122  	var canonicalBuffer bytes.Buffer
   123  	// Use an inner function so we can defer the Close()
   124  	if err := func() error {
   125  		ew, err := armor.Encode(&canonicalBuffer, openpgp.SignatureType, nil)
   126  		if err != nil {
   127  			return fmt.Errorf("error encoding canonical value of PGP signature: %w", err)
   128  		}
   129  		defer ew.Close()
   130  
   131  		if _, err := io.Copy(ew, bytes.NewReader(s.signature)); err != nil {
   132  			return fmt.Errorf("error generating canonical value of PGP signature: %w", err)
   133  		}
   134  		return nil
   135  	}(); err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	return canonicalBuffer.Bytes(), nil
   140  }
   141  
   142  // Verify implements the pki.Signature interface
   143  func (s Signature) Verify(r io.Reader, k interface{}, _ ...sigsig.VerifyOption) error {
   144  	if len(s.signature) == 0 {
   145  		return errors.New("PGP signature has not been initialized")
   146  	}
   147  
   148  	key, ok := k.(*PublicKey)
   149  	if !ok {
   150  		return errors.New("cannot use Verify with a non-PGP signature")
   151  	}
   152  	if len(key.key) == 0 {
   153  		return errors.New("PGP public key has not been initialized")
   154  	}
   155  
   156  	verifyFn := openpgp.CheckDetachedSignature
   157  	if s.isArmored {
   158  		verifyFn = openpgp.CheckArmoredDetachedSignature
   159  	}
   160  
   161  	if _, err := verifyFn(key.key, r, bytes.NewReader(s.signature)); err != nil {
   162  		return err
   163  	}
   164  
   165  	return nil
   166  }
   167  
   168  // PublicKey Public Key that follows the PGP standard; supports both armored & binary detached signatures
   169  type PublicKey struct {
   170  	key openpgp.EntityList
   171  }
   172  
   173  // NewPublicKey implements the pki.PublicKey interface
   174  func NewPublicKey(r io.Reader) (*PublicKey, error) {
   175  	var k PublicKey
   176  	var inputBuffer bytes.Buffer
   177  
   178  	startToken := []byte(`-----BEGIN PGP`)
   179  	endToken := []byte(`-----END PGP`)
   180  
   181  	bufferedReader := bufio.NewReader(r)
   182  	armorCheck, err := bufferedReader.Peek(len(startToken))
   183  	if err != nil {
   184  		return nil, fmt.Errorf("unable to read PGP public key: %w", err)
   185  	}
   186  	if bytes.Equal(startToken, armorCheck) {
   187  		// looks like we have armored input
   188  		scan := bufio.NewScanner(bufferedReader)
   189  		scan.Split(bufio.ScanLines)
   190  
   191  		for scan.Scan() {
   192  			line := scan.Bytes()
   193  			inputBuffer.Write(line)
   194  			fmt.Fprintf(&inputBuffer, "\n")
   195  
   196  			if bytes.HasPrefix(line, endToken) {
   197  				// we have a complete armored message; process it
   198  				keyBlock, err := armor.Decode(&inputBuffer)
   199  				if err == nil {
   200  					if keyBlock.Type != openpgp.PublicKeyType && keyBlock.Type != openpgp.PrivateKeyType {
   201  						return nil, errors.New("invalid PGP type detected")
   202  					}
   203  					keys, err := openpgp.ReadKeyRing(keyBlock.Body)
   204  					if err != nil {
   205  						return nil, fmt.Errorf("error reading PGP public key: %w", err)
   206  					}
   207  					if k.key == nil {
   208  						k.key = keys
   209  					} else {
   210  						k.key = append(k.key, keys...)
   211  					}
   212  					inputBuffer.Reset()
   213  				} else {
   214  					return nil, fmt.Errorf("invalid PGP public key provided: %w", err)
   215  				}
   216  			}
   217  		}
   218  	} else {
   219  		// process as binary
   220  		k.key, err = openpgp.ReadKeyRing(bufferedReader)
   221  		if err != nil {
   222  			return nil, fmt.Errorf("error reading binary PGP public key: %w", err)
   223  		}
   224  	}
   225  
   226  	if len(k.key) == len(k.key.DecryptionKeys()) {
   227  		return nil, errors.New("no PGP public keys could be read")
   228  	}
   229  
   230  	return &k, nil
   231  }
   232  
   233  // FetchPublicKey implements pki.PublicKey interface
   234  func FetchPublicKey(ctx context.Context, url string) (*PublicKey, error) {
   235  	//TODO: detect if url is hkp and adjust accordingly
   236  	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
   237  	if err != nil {
   238  		return nil, fmt.Errorf("error fetching PGP public key: %w", err)
   239  	}
   240  	client := &http.Client{}
   241  	resp, err := client.Do(req)
   242  	if err != nil {
   243  		return nil, fmt.Errorf("error fetching PGP public key: %w", err)
   244  	}
   245  	defer resp.Body.Close()
   246  
   247  	key, err := NewPublicKey(resp.Body)
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  
   252  	return key, nil
   253  }
   254  
   255  // CanonicalValue implements the pki.PublicKey interface
   256  func (k PublicKey) CanonicalValue() ([]byte, error) {
   257  	if k.key == nil {
   258  		return nil, errors.New("PGP public key has not been initialized")
   259  	}
   260  
   261  	var canonicalBuffer bytes.Buffer
   262  
   263  	// Use an inner function so we can defer the close()
   264  	if err := func() error {
   265  		armoredWriter, err := armor.Encode(&canonicalBuffer, openpgp.PublicKeyType, nil)
   266  		if err != nil {
   267  			return fmt.Errorf("error generating canonical value of PGP public key: %w", err)
   268  		}
   269  		defer armoredWriter.Close()
   270  
   271  		for _, entity := range k.key {
   272  			if err := entity.Serialize(armoredWriter); err != nil {
   273  				return fmt.Errorf("error generating canonical value of PGP public key: %w", err)
   274  			}
   275  		}
   276  		return nil
   277  	}(); err != nil {
   278  		return nil, err
   279  	}
   280  
   281  	return canonicalBuffer.Bytes(), nil
   282  }
   283  
   284  func (k PublicKey) KeyRing() (openpgp.KeyRing, error) {
   285  	if k.key == nil {
   286  		return nil, errors.New("PGP public key has not been initialized")
   287  	}
   288  
   289  	return k.key, nil
   290  }
   291  
   292  // EmailAddresses implements the pki.PublicKey interface
   293  func (k PublicKey) EmailAddresses() []string {
   294  	var names []string
   295  	// Extract from cert
   296  	for _, entity := range k.key {
   297  		for _, identity := range entity.Identities {
   298  			if govalidator.IsEmail(identity.UserId.Email) {
   299  				names = append(names, identity.UserId.Email)
   300  			}
   301  		}
   302  	}
   303  	return names
   304  }
   305  
   306  // Subjects implements the pki.PublicKey interface
   307  func (k PublicKey) Subjects() []string {
   308  	return k.EmailAddresses()
   309  }
   310  
   311  // Identities implements the pki.PublicKey interface
   312  func (k PublicKey) Identities() ([]identity.Identity, error) {
   313  	var ids []identity.Identity
   314  	for _, entity := range k.key {
   315  		var keys []*packet.PublicKey
   316  		keys = append(keys, entity.PrimaryKey)
   317  		for _, subKey := range entity.Subkeys {
   318  			keys = append(keys, subKey.PublicKey)
   319  		}
   320  		for _, pk := range keys {
   321  			pubKey := pk.PublicKey
   322  			// Only process supported types. Will ignore DSA
   323  			// and ElGamal keys.
   324  			// TODO: For a V2 PGP type, enforce on upload
   325  			switch pubKey.(type) {
   326  			case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey:
   327  			default:
   328  				continue
   329  			}
   330  			pkixKey, err := cryptoutils.MarshalPublicKeyToDER(pubKey)
   331  			if err != nil {
   332  				return nil, err
   333  			}
   334  			ids = append(ids, identity.Identity{
   335  				Crypto:      pubKey,
   336  				Raw:         pkixKey,
   337  				Fingerprint: hex.EncodeToString(pk.Fingerprint[:]),
   338  			})
   339  		}
   340  	}
   341  	return ids, nil
   342  }
   343  

View as plain text