...

Source file src/github.com/sassoftware/relic/lib/signappx/bundle.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/x509"
    23  	"encoding/xml"
    24  	"errors"
    25  	"fmt"
    26  	"io"
    27  	"strings"
    28  
    29  	"github.com/beevik/etree"
    30  
    31  	"github.com/sassoftware/relic/lib/x509tools"
    32  )
    33  
    34  type bundleManifest struct {
    35  	XMLName       xml.Name `xml:"http://schemas.microsoft.com/appx/2013/bundle Bundle"`
    36  	SchemaVersion string   `xml:",attr"`
    37  
    38  	Identity appxIdentity
    39  	Packages []bundlePackage `xml:"Packages>Package"`
    40  
    41  	Etree *etree.Document `xml:"-"`
    42  }
    43  
    44  type bundlePackage struct {
    45  	Type         string `xml:",attr"`
    46  	Version      string `xml:",attr"`
    47  	Architecture string `xml:",attr"`
    48  	FileName     string `xml:",attr"`
    49  	Offset       int64  `xml:",attr"`
    50  	Size         uint64 `xml:",attr"`
    51  }
    52  
    53  func verifyBundle(r io.ReaderAt, files zipFiles, sig *AppxSignature, skipDigests bool) error {
    54  	blob, err := readZipFile(files[bundleManifestFile])
    55  	if err != nil {
    56  		return fmt.Errorf("bundle manifest: %s", err)
    57  	}
    58  	var bundle bundleManifest
    59  	if err := xml.Unmarshal(blob, &bundle); err != nil {
    60  		return fmt.Errorf("bundle manifest: %s", err)
    61  	}
    62  	packages := make(map[string]int)
    63  	for i, pkg := range bundle.Packages {
    64  		packages[pkg.FileName] = i
    65  	}
    66  	sig.Bundled = make(map[string]*AppxSignature)
    67  	publisher := x509tools.FormatPkixName(sig.Signature.Certificate.RawSubject, x509tools.NameStyleMsOsco)
    68  	if bundle.Identity.Publisher != publisher {
    69  		return fmt.Errorf("bundle manifest: publisher identity mismatch:\nexpected: %s\nactual: %s", publisher, bundle.Identity.Publisher)
    70  	}
    71  	for _, zf := range files {
    72  		if !strings.HasSuffix(zf.Name, ".appx") {
    73  			continue
    74  		}
    75  		if zf.Method != zip.Store {
    76  			return errors.New("bundle manifest: contains compressed appx")
    77  		}
    78  		dosname := strings.Replace(zf.Name, "/", "\\", -1)
    79  		pkgIndex, ok := packages[dosname]
    80  		if !ok {
    81  			return fmt.Errorf("bundle manifest: missing file %s", zf.Name)
    82  		}
    83  		packages[dosname] = -1 // mark as seen
    84  		pkg := bundle.Packages[pkgIndex]
    85  
    86  		offset, err := zf.DataOffset()
    87  		if err != nil {
    88  			return fmt.Errorf("bundle manifest: %s", err)
    89  		}
    90  		if pkg.Offset != offset {
    91  			return fmt.Errorf("bundle manifest: %s claimed offset of %d but actual offset is %d", zf.Name, pkg.Offset, offset)
    92  		} else if pkg.Size != zf.UncompressedSize64 {
    93  			return fmt.Errorf("bundle manifest: %s claimed size of %d but actual size is %d", zf.Name, pkg.Size, zf.UncompressedSize64)
    94  		}
    95  		nested := io.NewSectionReader(r, offset, int64(zf.UncompressedSize64))
    96  		nestedSig, err := Verify(nested, int64(zf.UncompressedSize64), skipDigests)
    97  		if err != nil {
    98  			return fmt.Errorf("bundled file %s: %s", zf.Name, err)
    99  		}
   100  		if !bytes.Equal(nestedSig.Signature.Certificate.Raw, sig.Signature.Certificate.Raw) {
   101  			return fmt.Errorf("bundled file %s signed by different publisher", zf.Name)
   102  		}
   103  		sig.Bundled[zf.Name] = nestedSig
   104  	}
   105  	for name, unseen := range packages {
   106  		if unseen >= 0 {
   107  			return fmt.Errorf("bundle missing file: %s", name)
   108  		}
   109  	}
   110  	return nil
   111  }
   112  
   113  func parseBundle(blob []byte) (*bundleManifest, error) {
   114  	manifest := new(bundleManifest)
   115  	if err := xml.Unmarshal(blob, manifest); err != nil {
   116  		return nil, err
   117  	}
   118  	manifest.Etree = etree.NewDocument()
   119  	if err := manifest.Etree.ReadFromBytes(blob); err != nil {
   120  		return nil, err
   121  	}
   122  	return manifest, nil
   123  }
   124  
   125  func (m *bundleManifest) SetPublisher(cert *x509.Certificate) {
   126  	subj := x509tools.FormatPkixName(cert.RawSubject, x509tools.NameStyleMsOsco)
   127  	m.Identity.Publisher = subj
   128  	el := m.Etree.FindElement("Bundle/Identity")
   129  	if el != nil {
   130  		el.CreateAttr("Publisher", subj)
   131  	}
   132  }
   133  
   134  func (m *bundleManifest) Marshal() ([]byte, error) {
   135  	return m.Etree.WriteToBytes()
   136  }
   137  

View as plain text