1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
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
112 default:
113
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
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
140
141
142
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
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
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