...

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

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

     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 signappx
    18  
    19  import (
    20  	"archive/zip"
    21  	"bytes"
    22  	"crypto/hmac"
    23  	"errors"
    24  	"fmt"
    25  	"io"
    26  	"io/ioutil"
    27  
    28  	"github.com/sassoftware/relic/lib/authenticode"
    29  	"github.com/sassoftware/relic/lib/pkcs7"
    30  	"github.com/sassoftware/relic/lib/pkcs9"
    31  	"github.com/sassoftware/relic/lib/x509tools"
    32  	"github.com/sassoftware/relic/signers/sigerrors"
    33  )
    34  
    35  func Verify(r io.ReaderAt, size int64, skipDigests bool) (*AppxSignature, error) {
    36  	inz, err := zip.NewReader(r, size)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	files := make(zipFiles, len(inz.File))
    41  	for _, file := range inz.File {
    42  		files[file.Name] = file
    43  	}
    44  	sig, err := readSignature(files[appxSignature])
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	sig.IsBundle = files[bundleManifestFile] != nil
    49  	if err := verifyFile(files, sig, "AXBM", appxBlockMap); err != nil {
    50  		return nil, err
    51  	}
    52  	if err := verifyFile(files, sig, "AXCI", appxCodeIntegrity); err != nil {
    53  		return nil, err
    54  	}
    55  	if err := verifyFile(files, sig, "AXCT", appxContentTypes); err != nil {
    56  		return nil, err
    57  	}
    58  	if err := verifyBlockMap(inz, files, skipDigests); err != nil {
    59  		return nil, err
    60  	}
    61  	if err := verifyCatalog(files[appxCodeIntegrity], sig); err != nil {
    62  		return nil, err
    63  	}
    64  	if err := verifyMeta(r, size, sig, skipDigests); err != nil {
    65  		return nil, err
    66  	}
    67  	if sig.IsBundle {
    68  		if err := verifyBundle(r, files, sig, skipDigests); err != nil {
    69  			return nil, err
    70  		}
    71  	} else {
    72  		if err := checkManifest(files, sig); err != nil {
    73  			return nil, err
    74  		}
    75  	}
    76  	return sig, nil
    77  }
    78  
    79  func readSignature(zf *zip.File) (*AppxSignature, error) {
    80  	if zf == nil {
    81  		return nil, sigerrors.NotSignedError{Type: "appx"}
    82  	}
    83  	blob, err := readZipFile(zf)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	if !bytes.HasPrefix(blob, []byte("PKCX")) {
    88  		return nil, errors.New("invalid appx signature")
    89  	}
    90  	psd, err := pkcs7.Unmarshal(blob[4:])
    91  	if err != nil {
    92  		return nil, fmt.Errorf("invalid appx signature: %s", err)
    93  	}
    94  	if !psd.Content.ContentInfo.ContentType.Equal(authenticode.OidSpcIndirectDataContent) {
    95  		return nil, fmt.Errorf("invalid appx signature: %s", "not an authenticode signature")
    96  	}
    97  	pksig, err := psd.Content.Verify(nil, false)
    98  	if err != nil {
    99  		return nil, fmt.Errorf("invalid appx signature: %s", err)
   100  	}
   101  	ts, err := pkcs9.VerifyOptionalTimestamp(pksig)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	indirect := new(authenticode.SpcIndirectDataContentMsi)
   106  	if err := psd.Content.ContentInfo.Unmarshal(indirect); err != nil {
   107  		return nil, fmt.Errorf("invalid appx signature: %s", err)
   108  	}
   109  	hash, err := x509tools.PkixDigestToHashE(indirect.MessageDigest.DigestAlgorithm)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	digests := indirect.MessageDigest.Digest
   114  	if !bytes.HasPrefix(digests, []byte("APPX")) {
   115  		return nil, errors.New("invalid appx signature")
   116  	}
   117  	digests = digests[4:]
   118  	digestmap := make(map[string][]byte)
   119  	for len(digests) > 0 {
   120  		if len(digests) < 4+hash.Size() {
   121  			return nil, errors.New("invalid appx signature")
   122  		}
   123  		name := string(digests[:4])
   124  		digestmap[name] = digests[4 : 4+hash.Size()]
   125  		digests = digests[4+hash.Size():]
   126  	}
   127  	return &AppxSignature{
   128  		Signature:  &ts,
   129  		Hash:       hash,
   130  		HashValues: digestmap,
   131  	}, nil
   132  }
   133  
   134  func verifyFile(files zipFiles, sig *AppxSignature, tag, name string) error {
   135  	expected := sig.HashValues[tag]
   136  	zf := files[name]
   137  	if zf == nil {
   138  		if expected == nil {
   139  			return nil
   140  		}
   141  		return fmt.Errorf("appx missing signed file: %s", name)
   142  	} else if expected == nil {
   143  		return fmt.Errorf("appx missing signature for file: %s", name)
   144  	}
   145  	r, err := zf.Open()
   146  	if err != nil {
   147  		return err
   148  	}
   149  	d := sig.Hash.New()
   150  	if _, err := io.Copy(d, r); err != nil {
   151  		return err
   152  	}
   153  	if err := r.Close(); err != nil {
   154  		return err
   155  	}
   156  	calc := d.Sum(nil)
   157  	if !hmac.Equal(calc, expected) {
   158  		return fmt.Errorf("appx digest mismatch for %s: calculated %x != found %x", name, calc, expected)
   159  	}
   160  	return nil
   161  }
   162  
   163  func readZipFile(zf *zip.File) ([]byte, error) {
   164  	if zf == nil {
   165  		return nil, errors.New("file not found")
   166  	}
   167  	r, err := zf.Open()
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  	blob, err := ioutil.ReadAll(r)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	if err := r.Close(); err != nil {
   176  		return nil, err
   177  	}
   178  	return blob, nil
   179  }
   180  
   181  func verifyCatalog(zf *zip.File, sig *AppxSignature) error {
   182  	if zf == nil {
   183  		if sig.IsBundle {
   184  			return nil
   185  		}
   186  		return errors.New("missing security catalog")
   187  	}
   188  	blob, err := readZipFile(zf)
   189  	if err != nil {
   190  		return err
   191  	}
   192  	psd, err := pkcs7.Unmarshal(blob)
   193  	if err != nil {
   194  		return fmt.Errorf("security catalog: %s", err)
   195  	}
   196  	if !psd.Content.ContentInfo.ContentType.Equal(authenticode.OidCertTrustList) {
   197  		return fmt.Errorf("security catalog: %s", "not a security catalog")
   198  	}
   199  	pksig, err := psd.Content.Verify(nil, false)
   200  	if err != nil {
   201  		return fmt.Errorf("security catalog: %s", err)
   202  	}
   203  	ts, err := pkcs9.VerifyOptionalTimestamp(pksig)
   204  	if err != nil {
   205  		return fmt.Errorf("security catalog: %s", err)
   206  	}
   207  	if !bytes.Equal(ts.Certificate.Raw, sig.Signature.Certificate.Raw) {
   208  		return fmt.Errorf("security catalog: %s", "catalog signed by different certificate than appx")
   209  	}
   210  	// TODO: figure out what the things in the catalog actually are
   211  	return nil
   212  }
   213  

View as plain text