...

Source file src/github.com/sassoftware/relic/lib/authenticode/peverify.go

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

     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 authenticode
    18  
    19  import (
    20  	"bytes"
    21  	"crypto"
    22  	"crypto/hmac"
    23  	"encoding/asn1"
    24  	"encoding/binary"
    25  	"errors"
    26  	"fmt"
    27  	"io"
    28  	"io/ioutil"
    29  
    30  	"github.com/sassoftware/relic/lib/pkcs7"
    31  	"github.com/sassoftware/relic/lib/pkcs9"
    32  	"github.com/sassoftware/relic/lib/x509tools"
    33  	"github.com/sassoftware/relic/signers/sigerrors"
    34  )
    35  
    36  type PESignature struct {
    37  	pkcs9.TimestampedSignature
    38  	Indirect      *SpcIndirectDataContentPe
    39  	ImageHashFunc crypto.Hash
    40  	PageHashes    []byte
    41  	PageHashFunc  crypto.Hash
    42  }
    43  
    44  // Extract and verify the signature from a PE/COFF image file. Does not check X509 chains.
    45  func VerifyPE(r io.ReadSeeker, skipDigests bool) ([]PESignature, error) {
    46  	hvals, err := findSignatures(r)
    47  	if err != nil {
    48  		return nil, err
    49  	} else if hvals.certSize == 0 {
    50  		return nil, sigerrors.NotSignedError{Type: "PECOFF"}
    51  	}
    52  	// Read certificate table
    53  	sigblob := make([]byte, hvals.certSize)
    54  	r.Seek(hvals.certStart, 0)
    55  	if _, err := io.ReadFull(r, sigblob); err != nil {
    56  		return nil, err
    57  	}
    58  	// Parse and verify signatures
    59  	if skipDigests {
    60  		r = nil
    61  	}
    62  	return checkSignatures(sigblob, r)
    63  }
    64  
    65  func findSignatures(r io.ReadSeeker) (*peHeaderValues, error) {
    66  	if _, err := r.Seek(0, 0); err != nil {
    67  		return nil, err
    68  	}
    69  	d := ioutil.Discard
    70  	peStart, err := readDosHeader(r, d)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	r.Seek(peStart, 0)
    75  	fh, err := readCoffHeader(r, d)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	return readOptHeader(r, d, peStart, fh)
    80  }
    81  
    82  func checkSignatures(blob []byte, image io.ReadSeeker) ([]PESignature, error) {
    83  	values := make(map[crypto.Hash][]byte)
    84  	phvalues := make(map[crypto.Hash][]byte)
    85  	allhashes := make(map[crypto.Hash]bool)
    86  	sigs := make([]PESignature, 0, 1)
    87  	for len(blob) != 0 {
    88  		wLen := binary.LittleEndian.Uint32(blob[:4])
    89  		end := (int(wLen) + 7) / 8 * 8
    90  		size := int(wLen) - 8
    91  		if end > len(blob) || size < 0 {
    92  			return nil, errors.New("invalid certificate table")
    93  		}
    94  		cert := blob[8 : 8+size]
    95  		blob = blob[end:]
    96  
    97  		sig, err := checkSignature(cert)
    98  		if err != nil {
    99  			return nil, err
   100  		}
   101  		allhashes[sig.ImageHashFunc] = true
   102  		if len(sig.PageHashes) > 0 {
   103  			phvalues[sig.PageHashFunc] = sig.PageHashes
   104  			allhashes[sig.PageHashFunc] = true
   105  		}
   106  		sigs = append(sigs, *sig)
   107  		imageDigest := sig.Indirect.MessageDigest.Digest
   108  		if existing := values[sig.ImageHashFunc]; existing == nil {
   109  			values[sig.ImageHashFunc] = imageDigest
   110  		} else if !hmac.Equal(imageDigest, existing) {
   111  			// they can't both be right...
   112  			return nil, fmt.Errorf("digest mismatch: %x != %x", imageDigest, existing)
   113  		}
   114  	}
   115  	if image == nil {
   116  		return sigs, nil
   117  	}
   118  	for hash := range allhashes {
   119  		imagehash := values[hash]
   120  		pagehashes := phvalues[hash]
   121  		if _, err := image.Seek(0, 0); err != nil {
   122  			return nil, err
   123  		}
   124  		doPageHashes := len(pagehashes) > 0
   125  		digest, err := DigestPE(image, hash, doPageHashes)
   126  		if err != nil {
   127  			return nil, err
   128  		}
   129  		if imagehash != nil && !hmac.Equal(digest.Imprint, imagehash) {
   130  			return nil, fmt.Errorf("digest mismatch: %x != %x", digest.Imprint, imagehash)
   131  		}
   132  		if pagehashes != nil && !hmac.Equal(digest.PageHashes, pagehashes) {
   133  			return nil, fmt.Errorf("page hash mismatch")
   134  		}
   135  	}
   136  	return sigs, nil
   137  }
   138  
   139  func checkSignature(der []byte) (*PESignature, error) {
   140  	psd, err := pkcs7.Unmarshal(der)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	if !psd.Content.ContentInfo.ContentType.Equal(OidSpcIndirectDataContent) {
   145  		return nil, errors.New("not an authenticode signature")
   146  	}
   147  	sig, err := psd.Content.Verify(nil, false)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	ts, err := pkcs9.VerifyOptionalTimestamp(sig)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	indirect := new(SpcIndirectDataContentPe)
   156  	if err := psd.Content.ContentInfo.Unmarshal(indirect); err != nil {
   157  		return nil, err
   158  	}
   159  	hash, err := x509tools.PkixDigestToHashE(indirect.MessageDigest.DigestAlgorithm)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	pesig := &PESignature{
   164  		TimestampedSignature: ts,
   165  		Indirect:             indirect,
   166  		ImageHashFunc:        hash,
   167  	}
   168  	if err := readPageHashes(pesig); err != nil {
   169  		return nil, err
   170  	}
   171  	return pesig, nil
   172  }
   173  
   174  func readPageHashes(sig *PESignature) error {
   175  	serObj := sig.Indirect.Data.Value.File.Moniker
   176  	if !bytes.Equal(serObj.ClassID, SpcUUIDPageHashes) {
   177  		// not present
   178  		return nil
   179  	}
   180  	// unnecessary SET wrapped around the solitary attribute SEQ
   181  	var attrRaw asn1.RawValue
   182  	if _, err := asn1.Unmarshal(serObj.SerializedData, &attrRaw); err != nil {
   183  		return err
   184  	}
   185  	var attr SpcAttributePageHashes
   186  	if _, err := asn1.Unmarshal(attrRaw.Bytes, &attr); err != nil {
   187  		return err
   188  	}
   189  	switch {
   190  	case attr.Type.Equal(OidSpcPageHashV1):
   191  		sig.PageHashFunc = crypto.SHA1
   192  	case attr.Type.Equal(OidSpcPageHashV2):
   193  		sig.PageHashFunc = crypto.SHA256
   194  	default:
   195  		return errors.New("unknown page hash format")
   196  	}
   197  	// unnecessary SET wrapped around the octets too
   198  	sig.PageHashes = attr.Hashes[0]
   199  	if len(sig.PageHashes) == 0 || len(sig.PageHashes)%(4+sig.PageHashFunc.Size()) != 0 {
   200  		return errors.New("malformed page hash")
   201  	}
   202  	return nil
   203  }
   204  

View as plain text