...

Source file src/github.com/sassoftware/relic/lib/pkcs7/verify.go

Documentation: github.com/sassoftware/relic/lib/pkcs7

     1  //
     2  // Copyright (c) SAS Institute Inc.
     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  
    17  package pkcs7
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/hmac"
    22  	"crypto/rsa"
    23  	"crypto/x509"
    24  	"fmt"
    25  	"time"
    26  
    27  	"github.com/pkg/errors"
    28  	"github.com/sassoftware/relic/lib/x509tools"
    29  )
    30  
    31  type Signature struct {
    32  	SignerInfo    *SignerInfo
    33  	Certificate   *x509.Certificate
    34  	Intermediates []*x509.Certificate
    35  }
    36  
    37  // Verify the content in a SignedData structure. External content may be
    38  // provided if it is a detached signature. Information about the signature is
    39  // returned, however X509 chains are not validated. Call VerifyChain() to
    40  // complete the verification process.
    41  //
    42  // If skipDigests is true, then the main content section is not checked, but
    43  // the SignerInfos are still checked for a valid signature.
    44  func (sd *SignedData) Verify(externalContent []byte, skipDigests bool) (Signature, error) {
    45  	var content []byte
    46  	if !skipDigests {
    47  		var err error
    48  		content, err = sd.ContentInfo.Bytes()
    49  		if err != nil {
    50  			return Signature{}, err
    51  		} else if content == nil {
    52  			if externalContent == nil {
    53  				return Signature{}, errors.New("pkcs7: missing content")
    54  			}
    55  			content = externalContent
    56  		} else if externalContent != nil {
    57  			if !bytes.Equal(externalContent, content) {
    58  				return Signature{}, errors.New("pkcs7: internal and external content were both provided but are not equal")
    59  			}
    60  		}
    61  	}
    62  	certs, err := sd.Certificates.Parse()
    63  	if err != nil {
    64  		return Signature{}, fmt.Errorf("pkcs7: %s", err)
    65  	} else if len(certs) == 0 {
    66  		return Signature{}, errors.New("pkcs7: certificate missing from signedData")
    67  	}
    68  	var cert *x509.Certificate
    69  	var sig Signature
    70  	for _, si := range sd.SignerInfos {
    71  		cert, err = si.Verify(content, skipDigests, certs)
    72  		if err != nil {
    73  			return Signature{}, err
    74  		}
    75  		sig = Signature{&si, cert, certs}
    76  	}
    77  	return sig, nil
    78  }
    79  
    80  // Find the certificate that signed this SignerInfo from the bucket of certs
    81  func (si *SignerInfo) FindCertificate(certs []*x509.Certificate) (*x509.Certificate, error) {
    82  	is := si.IssuerAndSerialNumber
    83  	for _, cert := range certs {
    84  		if bytes.Equal(cert.RawIssuer, is.IssuerName.FullBytes) && cert.SerialNumber.Cmp(is.SerialNumber) == 0 {
    85  			return cert, nil
    86  		}
    87  	}
    88  	return nil, errors.New("pkcs7: certificate missing from signedData")
    89  }
    90  
    91  // Verify the signature contained in this SignerInfo and return the leaf
    92  // certificate. X509 chains are not validated.
    93  func (si *SignerInfo) Verify(content []byte, skipDigests bool, certs []*x509.Certificate) (*x509.Certificate, error) {
    94  	hash, err := x509tools.PkixDigestToHashE(si.DigestAlgorithm)
    95  	if err != nil {
    96  		return nil, errors.Wrap(err, "pkcs7")
    97  	}
    98  	var digest []byte
    99  	if !skipDigests {
   100  		w := hash.New()
   101  		w.Write(content)
   102  		digest = w.Sum(nil)
   103  	}
   104  	if len(si.AuthenticatedAttributes) != 0 {
   105  		// check the content digest against the messageDigest attribute
   106  		var md []byte
   107  		if err := si.AuthenticatedAttributes.GetOne(OidAttributeMessageDigest, &md); err != nil {
   108  			return nil, err
   109  		} else if digest != nil && !hmac.Equal(md, digest) {
   110  			return nil, errors.New("pkcs7: content digest does not match")
   111  		}
   112  		// now pivot to verifying the hash over the authenticated attributes
   113  		w := hash.New()
   114  		attrbytes, err := si.AuthenticatedAttributes.Bytes()
   115  		if err != nil {
   116  			return nil, err
   117  		}
   118  		w.Write(attrbytes)
   119  		digest = w.Sum(nil)
   120  	} // otherwise the content hash is verified directly
   121  	cert, err := si.FindCertificate(certs)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	// If skipDigests is set and AuthenticatedAttributes is not present then
   126  	// there's no digest to check the signature against. For RSA at least it's
   127  	// possible to decrypt the signature and check the padding but there's not
   128  	// much point to it.
   129  	if digest != nil {
   130  		err = x509tools.PkixVerify(cert.PublicKey, si.DigestAlgorithm, si.DigestEncryptionAlgorithm, digest, si.EncryptedDigest)
   131  		if err == rsa.ErrVerification {
   132  			// "Symantec Time Stamping Services Signer" seems to be emitting
   133  			// signatures without the AlgorithmIdentifier strucuture, so try
   134  			// without it.
   135  			err = x509tools.Verify(cert.PublicKey, 0, digest, si.EncryptedDigest)
   136  		}
   137  	}
   138  	return cert, err
   139  }
   140  
   141  // Verify the X509 chain from a signature against the given roots. extraCerts
   142  // will be added to the intermediates if provided. usage gives the certificate
   143  // usage required for the leaf certificate, or ExtKeyUsageAny otherwise. If a
   144  // PKCS#9 trusted timestamp was found, pass that timestamp in currentTime to
   145  // validate the chain as of the time of the signature.
   146  func (info Signature) VerifyChain(roots *x509.CertPool, extraCerts []*x509.Certificate, usage x509.ExtKeyUsage, currentTime time.Time) error {
   147  	pool := x509.NewCertPool()
   148  	for _, cert := range extraCerts {
   149  		pool.AddCert(cert)
   150  	}
   151  	for _, cert := range info.Intermediates {
   152  		pool.AddCert(cert)
   153  	}
   154  	opts := x509.VerifyOptions{
   155  		Intermediates: pool,
   156  		Roots:         roots,
   157  		CurrentTime:   currentTime,
   158  		KeyUsages:     []x509.ExtKeyUsage{usage},
   159  	}
   160  	_, err := info.Certificate.Verify(opts)
   161  	return err
   162  }
   163  

View as plain text