...

Source file src/github.com/sassoftware/relic/lib/signappx/tarappx.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  	"bytes"
    21  	"crypto"
    22  	"errors"
    23  	"fmt"
    24  	"hash"
    25  	"io"
    26  	"io/ioutil"
    27  	"strings"
    28  	"time"
    29  
    30  	"github.com/sassoftware/relic/lib/authenticode"
    31  	"github.com/sassoftware/relic/lib/zipslicer"
    32  )
    33  
    34  type AppxDigest struct {
    35  	Hash             crypto.Hash
    36  	blockMap         blockMap
    37  	manifest         *appxPackage
    38  	bundle           *bundleManifest
    39  	contentTypes     *ContentTypes
    40  	peDigests        []*authenticode.PEDigest
    41  	outz             *zipslicer.Directory
    42  	patchStart       int64
    43  	patchLen         int64
    44  	patchBuf         bytes.Buffer
    45  	mtime            time.Time
    46  	axpc             hash.Hash
    47  	axbm, axct, axci []byte
    48  }
    49  
    50  func DigestAppxTar(r io.Reader, hash crypto.Hash, doPageHash bool) (*AppxDigest, error) {
    51  	info := &AppxDigest{
    52  		Hash:         hash,
    53  		axpc:         hash.New(),
    54  		contentTypes: NewContentTypes(),
    55  		outz:         &zipslicer.Directory{},
    56  	}
    57  	if err := info.blockMap.SetHash(hash); err != nil {
    58  		return nil, err
    59  	}
    60  	inz, err := zipslicer.ReadZipTar(r)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	// digest non-signature-related files
    65  copyf:
    66  	for _, f := range inz.File {
    67  		switch f.Name {
    68  		case appxManifest, appxBlockMap, appxContentTypes, appxCodeIntegrity, appxSignature, bundleManifestFile:
    69  			info.patchStart = int64(f.Offset)
    70  			info.patchLen = inz.Size - info.patchStart
    71  			break copyf
    72  		default:
    73  			info.mtime = f.ModTime()
    74  			if err := info.digestFile(f, doPageHash); err != nil {
    75  				return nil, err
    76  			}
    77  			if _, err := info.outz.AddFile(f); err != nil {
    78  				return nil, err
    79  			}
    80  		}
    81  	}
    82  	idx := len(info.outz.File)
    83  	// parse signature-related files for later
    84  	for _, f := range inz.File[idx:] {
    85  		blob, err := readSlicerFile(f)
    86  		if err != nil {
    87  			return nil, err
    88  		}
    89  		switch f.Name {
    90  		case appxManifest:
    91  			manifest, err := parseManifest(blob)
    92  			if err != nil {
    93  				return nil, err
    94  			}
    95  			info.manifest = manifest
    96  		case bundleManifestFile:
    97  			manifest, err := parseBundle(blob)
    98  			if err != nil {
    99  				return nil, err
   100  			}
   101  			info.bundle = manifest
   102  		case appxBlockMap:
   103  			if err := info.blockMap.CopySizes(blob); err != nil {
   104  				return nil, err
   105  			}
   106  		case appxContentTypes:
   107  			if err := info.contentTypes.Parse(blob); err != nil {
   108  				return nil, err
   109  			}
   110  		case appxCodeIntegrity, appxSignature:
   111  			// discard
   112  		default:
   113  			// regular files can't come after files we mangle
   114  			return nil, fmt.Errorf("file %s is out of order", f.Name)
   115  		}
   116  	}
   117  	if info.manifest == nil && info.bundle == nil {
   118  		return nil, errors.New("missing manifest")
   119  	}
   120  	// drain the input to ensure the request is completely read before the response goes out
   121  	if _, err := io.Copy(ioutil.Discard, r); err != nil {
   122  		return nil, err
   123  	}
   124  	return info, nil
   125  }
   126  
   127  func readSlicerFile(f *zipslicer.File) ([]byte, error) {
   128  	r, err := f.Open()
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	blob, err := ioutil.ReadAll(r)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	return blob, r.Close()
   137  }
   138  
   139  // 4 different digests need to happen concurrently:
   140  // - AXPC for the raw zip headers and data
   141  // - blockmap 64KiB chunks of cooked data
   142  // - SHA1 and SHA256 digests over PE files for CodeIntegrity.cat
   143  func (i *AppxDigest) digestFile(f *zipslicer.File, doPageHash bool) error {
   144  	var peWriters []io.WriteCloser
   145  	var peResults []<-chan peDigestResult
   146  	var sink io.Writer
   147  	if strings.HasSuffix(f.Name, ".exe") || strings.HasSuffix(f.Name, ".dll") {
   148  		// DigestPE wants a Reader so make a pipe for each one and sink data into the pipes
   149  		peWriters, peResults = setupPeDigests(f.Name, i.Hash, doPageHash)
   150  		defer func() {
   151  			for _, w := range peWriters {
   152  				w.Close()
   153  			}
   154  		}()
   155  		mw := make([]io.Writer, len(peWriters))
   156  		for i, w := range peWriters {
   157  			mw[i] = w
   158  		}
   159  		sink = io.MultiWriter(mw...)
   160  	}
   161  	if err := i.blockMap.AddFile(f, i.axpc, sink); err != nil {
   162  		return err
   163  	}
   164  	if peWriters != nil {
   165  		for _, w := range peWriters {
   166  			w.Close()
   167  		}
   168  		for _, ch := range peResults {
   169  			result := <-ch
   170  			if result.err != nil {
   171  				return result.err
   172  			}
   173  			i.peDigests = append(i.peDigests, result.digest)
   174  		}
   175  	}
   176  	return nil
   177  }
   178  
   179  type peDigestResult struct {
   180  	digest *authenticode.PEDigest
   181  	err    error
   182  }
   183  
   184  func setupPeDigests(name string, hash crypto.Hash, doPageHash bool) (writers []io.WriteCloser, results []<-chan peDigestResult) {
   185  	w, r := setupPeDigest(name, hash, doPageHash)
   186  	writers = append(writers, w)
   187  	results = append(results, r)
   188  	if hash != crypto.SHA1 {
   189  		// SHA1 for catalog compatibility
   190  		w, r := setupPeDigest(name, crypto.SHA1, doPageHash)
   191  		writers = append(writers, w)
   192  		results = append(results, r)
   193  	}
   194  	return
   195  }
   196  
   197  func setupPeDigest(name string, hash crypto.Hash, doPageHash bool) (io.WriteCloser, <-chan peDigestResult) {
   198  	r, w := io.Pipe()
   199  	ch := make(chan peDigestResult, 1)
   200  	go func() {
   201  		digest, err := authenticode.DigestPE(r, hash, doPageHash)
   202  		if err != nil {
   203  			err = fmt.Errorf("failed to update CodeIntegrity catalog for %s: %s", name, err)
   204  		}
   205  		ch <- peDigestResult{digest, err}
   206  		r.CloseWithError(err)
   207  		close(ch)
   208  	}()
   209  	return w, ch
   210  }
   211  

View as plain text