1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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