...

Source file src/github.com/sassoftware/relic/lib/authenticode/msitar.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  	"archive/tar"
    21  	"bytes"
    22  	"crypto"
    23  	"io"
    24  	"io/ioutil"
    25  
    26  	"github.com/sassoftware/relic/lib/comdoc"
    27  )
    28  
    29  const (
    30  	msiTarExMeta     = "__exmeta"
    31  	msiTarStorageUID = "__storage_uid"
    32  )
    33  
    34  // Convert MSI to a tar archive in a form that can be digested and signed as a
    35  // stream
    36  func MsiToTar(cdf *comdoc.ComDoc, w io.Writer) error {
    37  	tw := tar.NewWriter(w)
    38  	// First write the metadata that is needed for an extended signature
    39  	var buf bytes.Buffer
    40  	if err := prehashMsiDir(cdf, cdf.RootStorage(), &buf); err != nil {
    41  		return err
    42  	}
    43  	if err := tarAddFile(tw, msiTarExMeta, buf.Bytes()); err != nil {
    44  		return err
    45  	}
    46  	// Now all of the files in the same order they would get digested in
    47  	if err := msiToTarDir(cdf, tw, cdf.RootStorage(), ""); err != nil {
    48  		return err
    49  	}
    50  	return tw.Close()
    51  }
    52  
    53  // Digset a tarball produced by MsiToTar
    54  func DigestMsiTar(r io.Reader, hash crypto.Hash, extended bool) ([]byte, error) {
    55  	tr := tar.NewReader(r)
    56  	d := hash.New()
    57  	for {
    58  		hdr, err := tr.Next()
    59  		if err == io.EOF {
    60  			break
    61  		} else if err != nil {
    62  			return nil, err
    63  		}
    64  		if hdr.Name == msiTarExMeta {
    65  			if !extended {
    66  				continue
    67  			}
    68  			exmeta, err := ioutil.ReadAll(tr)
    69  			if err != nil {
    70  				return nil, err
    71  			}
    72  			d2 := hash.New()
    73  			d2.Write(exmeta)
    74  			prehash := d2.Sum(nil)
    75  			d.Write(prehash)
    76  		} else if hdr.Name == msiDigitalSignature || hdr.Name == msiDigitalSignatureEx {
    77  			continue
    78  		}
    79  		if _, err := io.Copy(d, tr); err != nil {
    80  			return nil, err
    81  		}
    82  	}
    83  	return d.Sum(nil), nil
    84  }
    85  
    86  // Add a file with contents blob to an open tar.Writer
    87  func tarAddFile(tw *tar.Writer, name string, contents []byte) error {
    88  	hdr := &tar.Header{Name: name, Mode: 0644, Size: int64(len(contents))}
    89  	if err := tw.WriteHeader(hdr); err != nil {
    90  		return err
    91  	}
    92  	_, err := tw.Write(contents)
    93  	return err
    94  }
    95  
    96  // Recursively copy streams from a MSI directory (storage) to a tar.Writer
    97  func msiToTarDir(cdf *comdoc.ComDoc, tw *tar.Writer, parent *comdoc.DirEnt, path string) error {
    98  	files, err := cdf.ListDir(parent)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	sortMsiFiles(files)
   103  	for _, item := range files {
   104  		itemPath := path + msiDecodeName(item.Name())
   105  		switch item.Type {
   106  		case comdoc.DirStream:
   107  			r, err := cdf.ReadStream(item)
   108  			if err != nil {
   109  				return err
   110  			}
   111  			hdr := &tar.Header{
   112  				Name: itemPath,
   113  				Mode: 0644,
   114  				Size: int64(item.StreamSize),
   115  			}
   116  			if err := tw.WriteHeader(hdr); err != nil {
   117  				return err
   118  			}
   119  			if _, err := io.Copy(tw, r); err != nil {
   120  				return err
   121  			}
   122  		case comdoc.DirStorage:
   123  			if err := msiToTarDir(cdf, tw, item, itemPath+"/"); err != nil {
   124  				return err
   125  			}
   126  		}
   127  	}
   128  	// The UID of each storage gets hashed after its contents so include that, too
   129  	return tarAddFile(tw, path+msiTarStorageUID, parent.UID[:])
   130  }
   131  

View as plain text