...

Source file src/github.com/sigstore/timestamp-authority/pkg/verification/verify.go

Documentation: github.com/sigstore/timestamp-authority/pkg/verification

     1  //
     2  // Copyright 2022 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 verification
    17  
    18  import (
    19  	"bytes"
    20  	"crypto/x509"
    21  	"encoding/asn1"
    22  	"fmt"
    23  	"hash"
    24  	"io"
    25  	"math/big"
    26  
    27  	"github.com/digitorus/pkcs7"
    28  	"github.com/digitorus/timestamp"
    29  	"github.com/pkg/errors"
    30  )
    31  
    32  var (
    33  	// EKUOID is the Extended Key Usage OID, per RFC 5280
    34  	EKUOID = asn1.ObjectIdentifier{2, 5, 29, 37}
    35  )
    36  
    37  // VerifyOpts contains verification options for a RFC3161 timestamp
    38  type VerifyOpts struct {
    39  	// OID verifies that the TSR's OID has an expected value. Optional, used when
    40  	// an alternative OID was passed with a request to the TSA
    41  	OID asn1.ObjectIdentifier
    42  	// TSACertificate verifies that the TSR uses the TSACertificate as expected. Optional if the TSR contains the TSA certificate
    43  	TSACertificate *x509.Certificate
    44  	// Intermediates verifies the TSR's certificate. Optional, used for chain building
    45  	Intermediates []*x509.Certificate
    46  	// Roots is the set of trusted root certificates that verifies the TSR's certificate
    47  	Roots []*x509.Certificate
    48  	// Nonce verifies that the TSR contains the expected nonce. Optional, used when
    49  	// an optional nonce was passed with a request to the TSA
    50  	Nonce *big.Int
    51  	// CommonName verifies that the TSR certificate subject's Common Name matches the expected value. Optional
    52  	CommonName string
    53  }
    54  
    55  // Verify the TSR's certificate identifier matches a provided TSA certificate
    56  func verifyESSCertID(tsaCert *x509.Certificate, opts VerifyOpts) error {
    57  	if opts.TSACertificate == nil {
    58  		return nil
    59  	}
    60  
    61  	if !bytes.Equal(opts.TSACertificate.RawIssuer, tsaCert.RawIssuer) {
    62  		return fmt.Errorf("TSR cert issuer does not match provided TSA cert issuer")
    63  	}
    64  
    65  	if opts.TSACertificate.SerialNumber.Cmp(tsaCert.SerialNumber) != 0 {
    66  		return fmt.Errorf("TSR cert serial number does not match provided TSA cert serial number")
    67  	}
    68  
    69  	return nil
    70  }
    71  
    72  // Verify the leaf certificate's subject Common Name matches a provided Common Name
    73  func verifySubjectCommonName(cert *x509.Certificate, opts VerifyOpts) error {
    74  	if opts.CommonName == "" {
    75  		return nil
    76  	}
    77  
    78  	if cert.Subject.CommonName != opts.CommonName {
    79  		return fmt.Errorf("the certificate's subject Common Name %s does not match the provided Common Name %s", cert.Subject.CommonName, opts.CommonName)
    80  	}
    81  	return nil
    82  }
    83  
    84  // If embedded in the TSR, verify the TSR's leaf certificate matches a provided TSA certificate
    85  func verifyEmbeddedLeafCert(tsaCert *x509.Certificate, opts VerifyOpts) error {
    86  	if opts.TSACertificate != nil && !opts.TSACertificate.Equal(tsaCert) {
    87  		return fmt.Errorf("certificate embedded in the TSR does not match the provided TSA certificate")
    88  	}
    89  	return nil
    90  }
    91  
    92  // Verify the leaf's EKU is set to critical, per RFC 3161 2.3
    93  func verifyLeafCertCriticalEKU(cert *x509.Certificate) error {
    94  	var criticalEKU bool
    95  	for _, ext := range cert.Extensions {
    96  		if ext.Id.Equal(EKUOID) {
    97  			criticalEKU = ext.Critical
    98  			break
    99  		}
   100  	}
   101  	if !criticalEKU {
   102  		return errors.New("certificate must set EKU to critical")
   103  	}
   104  	return nil
   105  }
   106  
   107  func verifyLeafCert(ts timestamp.Timestamp, opts VerifyOpts) error {
   108  	if len(ts.Certificates) == 0 && opts.TSACertificate == nil {
   109  		return fmt.Errorf("leaf certificate must be present the in TSR or as a verify option")
   110  	}
   111  
   112  	errMsg := "failed to verify TSA certificate"
   113  
   114  	var leafCert *x509.Certificate
   115  	if len(ts.Certificates) != 0 {
   116  		leafCert = ts.Certificates[0]
   117  
   118  		err := verifyEmbeddedLeafCert(leafCert, opts)
   119  		if err != nil {
   120  			return fmt.Errorf("%s: %w", errMsg, err)
   121  		}
   122  	} else {
   123  		leafCert = opts.TSACertificate
   124  	}
   125  
   126  	err := verifyLeafCertCriticalEKU(leafCert)
   127  	if err != nil {
   128  		return fmt.Errorf("%s: %w", errMsg, err)
   129  	}
   130  
   131  	err = verifyESSCertID(leafCert, opts)
   132  	if err != nil {
   133  		return fmt.Errorf("%s: %w", errMsg, err)
   134  	}
   135  
   136  	err = verifySubjectCommonName(leafCert, opts)
   137  	if err != nil {
   138  		return fmt.Errorf("%s: %w", errMsg, err)
   139  	}
   140  
   141  	// verifies that the leaf certificate and any intermediate certificates
   142  	// have EKU set to only time stamping usage
   143  	err = verifyLeafAndIntermediatesTimestampingEKU(leafCert, opts)
   144  	if err != nil {
   145  		return fmt.Errorf("failed to verify EKU on leaf certificate: %w", err)
   146  	}
   147  
   148  	return nil
   149  }
   150  
   151  func verifyExtendedKeyUsage(cert *x509.Certificate) error {
   152  	certEKULen := len(cert.ExtKeyUsage)
   153  	if certEKULen != 1 {
   154  		return fmt.Errorf("certificate has %d extended key usages, expected only one", certEKULen)
   155  	}
   156  
   157  	if cert.ExtKeyUsage[0] != x509.ExtKeyUsageTimeStamping {
   158  		return fmt.Errorf("leaf certificate EKU is not set to TimeStamping as required")
   159  	}
   160  	return nil
   161  }
   162  
   163  // Verify the leaf and intermediate certificates (called "EKU chaining") all
   164  // have the extended key usage set to only time stamping usage
   165  func verifyLeafAndIntermediatesTimestampingEKU(leafCert *x509.Certificate, opts VerifyOpts) error {
   166  	err := verifyExtendedKeyUsage(leafCert)
   167  	if err != nil {
   168  		return fmt.Errorf("failed to verify EKU on leaf certificate: %w", err)
   169  	}
   170  
   171  	for _, cert := range opts.Intermediates {
   172  		err := verifyExtendedKeyUsage(cert)
   173  		if err != nil {
   174  			return fmt.Errorf("failed to verify EKU on intermediate certificate: %w", err)
   175  		}
   176  	}
   177  	return nil
   178  }
   179  
   180  // Verify the OID of the TSR matches an expected OID
   181  func verifyOID(oid []int, opts VerifyOpts) error {
   182  	if opts.OID == nil {
   183  		return nil
   184  	}
   185  	responseOID := opts.OID
   186  	if len(oid) != len(responseOID) {
   187  		return fmt.Errorf("OID lengths do not match")
   188  	}
   189  	for i, v := range oid {
   190  		if v != responseOID[i] {
   191  			return fmt.Errorf("OID content does not match")
   192  		}
   193  	}
   194  	return nil
   195  }
   196  
   197  // Verify the nonce - Mostly important for when the response is first returned
   198  func verifyNonce(requestNonce *big.Int, opts VerifyOpts) error {
   199  	if opts.Nonce == nil {
   200  		return nil
   201  	}
   202  	if opts.Nonce.Cmp(requestNonce) != 0 {
   203  		return fmt.Errorf("incoming nonce %d does not match TSR nonce %d", requestNonce, opts.Nonce)
   204  	}
   205  	return nil
   206  }
   207  
   208  // VerifyTimestampResponse the timestamp response using a timestamp certificate chain.
   209  func VerifyTimestampResponse(tsrBytes []byte, artifact io.Reader, opts VerifyOpts) (*timestamp.Timestamp, error) {
   210  	// Verify the status of the TSR does not contain an error
   211  	// handled by the timestamp.ParseResponse function
   212  	ts, err := timestamp.ParseResponse(tsrBytes)
   213  	if err != nil {
   214  		pe := timestamp.ParseError("")
   215  		if errors.As(err, &pe) {
   216  			return nil, fmt.Errorf("timestamp response is not valid: %w", err)
   217  		}
   218  		return nil, fmt.Errorf("error parsing response into Timestamp: %w", err)
   219  	}
   220  
   221  	// verify the timestamp response signature using the provided certificate pool
   222  	if err = verifyTSRWithChain(ts, opts); err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	if err = verifyNonce(ts.Nonce, opts); err != nil {
   227  		return nil, err
   228  	}
   229  
   230  	if err = verifyOID(ts.Policy, opts); err != nil {
   231  		return nil, err
   232  	}
   233  
   234  	if err = verifyLeafCert(*ts, opts); err != nil {
   235  		return nil, err
   236  	}
   237  
   238  	// verify the hash in the timestamp response matches the artifact hash
   239  	if err = verifyHashedMessages(ts.HashAlgorithm.New(), ts.HashedMessage, artifact); err != nil {
   240  		return nil, err
   241  	}
   242  
   243  	// if the parsed timestamp is verified, return the timestamp
   244  	return ts, nil
   245  }
   246  
   247  func verifyTSRWithChain(ts *timestamp.Timestamp, opts VerifyOpts) error {
   248  	p7Message, err := pkcs7.Parse(ts.RawToken)
   249  	if err != nil {
   250  		return fmt.Errorf("error parsing hashed message: %w", err)
   251  	}
   252  
   253  	if opts.Roots == nil || len(opts.Roots) == 0 {
   254  		return fmt.Errorf("no root certificates provided for verifying the certificate chain")
   255  	}
   256  
   257  	rootCertPool := x509.NewCertPool()
   258  	for _, cert := range opts.Roots {
   259  		rootCertPool.AddCert(cert)
   260  	}
   261  
   262  	intermediateCertPool := x509.NewCertPool()
   263  	for _, cert := range opts.Intermediates {
   264  		intermediateCertPool.AddCert(cert)
   265  	}
   266  
   267  	x509Opts := x509.VerifyOptions{
   268  		Roots:         rootCertPool,
   269  		Intermediates: intermediateCertPool,
   270  	}
   271  
   272  	// if the PCKS7 object does not have any certificates set in the
   273  	// Certificates field, the VerifyWithChain method will because it will be
   274  	// unable to find a leaf certificate associated with a signer. Since the
   275  	// leaf certificate issuer and serial number information is already part of
   276  	// the PKCS7 object, adding the leaf certificate to the Certificates field
   277  	// will allow verification to pass
   278  	if p7Message.Certificates == nil && opts.TSACertificate != nil {
   279  		p7Message.Certificates = []*x509.Certificate{opts.TSACertificate}
   280  	}
   281  
   282  	err = p7Message.VerifyWithOpts(x509Opts)
   283  	if err != nil {
   284  		return fmt.Errorf("error while verifying with chain: %w", err)
   285  	}
   286  
   287  	return nil
   288  }
   289  
   290  // Verify that the TSR's hashed message matches the digest of the artifact to be timestamped
   291  func verifyHashedMessages(hashAlg hash.Hash, hashedMessage []byte, artifactReader io.Reader) error {
   292  	h := hashAlg
   293  	if _, err := io.Copy(h, artifactReader); err != nil {
   294  		return fmt.Errorf("failed to create hash %w", err)
   295  	}
   296  	localHashedMsg := h.Sum(nil)
   297  
   298  	if !bytes.Equal(localHashedMsg, hashedMessage) {
   299  		return fmt.Errorf("hashed messages don't match")
   300  	}
   301  
   302  	return nil
   303  }
   304  

View as plain text