1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package authenticode
18
19 import (
20 "bytes"
21 "crypto"
22 "crypto/hmac"
23 "encoding/binary"
24 "errors"
25 "fmt"
26 "io"
27 "io/ioutil"
28 "sort"
29
30 "github.com/sassoftware/relic/lib/comdoc"
31 "github.com/sassoftware/relic/lib/pkcs7"
32 "github.com/sassoftware/relic/lib/pkcs9"
33 "github.com/sassoftware/relic/lib/x509tools"
34 "github.com/sassoftware/relic/signers/sigerrors"
35 )
36
37 type MSISignature struct {
38 pkcs9.TimestampedSignature
39 Indirect *SpcIndirectDataContentMsi
40 HashFunc crypto.Hash
41 }
42
43
44 func VerifyMSI(f io.ReaderAt, skipDigests bool) (*MSISignature, error) {
45 cdf, err := comdoc.ReadFile(f)
46 if err != nil {
47 return nil, err
48 }
49 var sig, exsig []byte
50 files, err := cdf.ListDir(nil)
51 if err != nil {
52 return nil, err
53 }
54 for _, item := range files {
55 name := item.Name()
56 if name == msiDigitalSignature {
57 r, err := cdf.ReadStream(item)
58 if err == nil {
59 sig, err = ioutil.ReadAll(r)
60 }
61 if err != nil {
62 return nil, err
63 }
64 } else if name == msiDigitalSignatureEx {
65 r, err := cdf.ReadStream(item)
66 if err == nil {
67 exsig, err = ioutil.ReadAll(r)
68 }
69 if err != nil {
70 return nil, err
71 }
72 }
73 }
74 if len(sig) == 0 {
75 return nil, sigerrors.NotSignedError{Type: "MSI"}
76 }
77 psd, err := pkcs7.Unmarshal(sig)
78 if err != nil {
79 return nil, err
80 }
81 if !psd.Content.ContentInfo.ContentType.Equal(OidSpcIndirectDataContent) {
82 return nil, errors.New("not an authenticode signature")
83 }
84 pksig, err := psd.Content.Verify(nil, false)
85 if err != nil {
86 return nil, err
87 }
88 ts, err := pkcs9.VerifyOptionalTimestamp(pksig)
89 if err != nil {
90 return nil, err
91 }
92 indirect := new(SpcIndirectDataContentMsi)
93 if err := psd.Content.ContentInfo.Unmarshal(indirect); err != nil {
94 return nil, err
95 }
96 hash, err := x509tools.PkixDigestToHashE(indirect.MessageDigest.DigestAlgorithm)
97 if err != nil {
98 return nil, err
99 }
100 msisig := &MSISignature{
101 TimestampedSignature: ts,
102 Indirect: indirect,
103 HashFunc: hash,
104 }
105 if !skipDigests {
106 imprint, prehash, err := DigestMSI(cdf, hash, exsig != nil)
107 if err != nil {
108 return nil, err
109 }
110 if exsig != nil && !hmac.Equal(prehash, exsig) {
111 return nil, fmt.Errorf("MSI extended digest mismatch: %x != %x", prehash, exsig)
112 }
113 if !hmac.Equal(imprint, indirect.MessageDigest.Digest) {
114 return nil, fmt.Errorf("MSI digest mismatch: %x != %x", imprint, indirect.MessageDigest.Digest)
115 }
116 }
117 return msisig, nil
118 }
119
120
121
122 func DigestMSI(cdf *comdoc.ComDoc, hash crypto.Hash, extended bool) (imprint, prehash []byte, err error) {
123 d := hash.New()
124 if extended {
125 prehash, err = PrehashMSI(cdf, hash)
126 if err != nil {
127 return nil, nil, err
128 }
129 d.Write(prehash)
130 }
131 if err := hashMsiDir(cdf, cdf.RootStorage(), d); err != nil {
132 return nil, nil, err
133 }
134 imprint = d.Sum(nil)
135 return
136 }
137
138
139 func PrehashMSI(cdf *comdoc.ComDoc, hash crypto.Hash) ([]byte, error) {
140 d2 := hash.New()
141 if err := prehashMsiDir(cdf, cdf.RootStorage(), d2); err != nil {
142 return nil, err
143 }
144 return d2.Sum(nil), nil
145 }
146
147
148 func hashMsiDir(cdf *comdoc.ComDoc, parent *comdoc.DirEnt, d io.Writer) error {
149 files, err := cdf.ListDir(parent)
150 if err != nil {
151 return err
152 }
153 sortMsiFiles(files)
154 for _, item := range files {
155 name := item.Name()
156 if name == msiDigitalSignature || name == msiDigitalSignatureEx {
157 continue
158 }
159 switch item.Type {
160 case comdoc.DirStream:
161 r, err := cdf.ReadStream(item)
162 if err != nil {
163 return err
164 }
165 if _, err := io.Copy(d, r); err != nil {
166 return err
167 }
168 case comdoc.DirStorage:
169 if err := hashMsiDir(cdf, item, d); err != nil {
170 return err
171 }
172 }
173 }
174 d.Write(parent.UID[:])
175 return nil
176 }
177
178
179 func prehashMsiDir(cdf *comdoc.ComDoc, parent *comdoc.DirEnt, d io.Writer) error {
180 files, err := cdf.ListDir(parent)
181 if err != nil {
182 return err
183 }
184 sortMsiFiles(files)
185 prehashMsiDirent(parent, d)
186 for _, item := range files {
187 name := item.Name()
188 if name == msiDigitalSignature || name == msiDigitalSignatureEx {
189 continue
190 }
191 switch item.Type {
192 case comdoc.DirStream:
193 prehashMsiDirent(item, d)
194 case comdoc.DirStorage:
195 if err := prehashMsiDir(cdf, item, d); err != nil {
196 return err
197 }
198 }
199 }
200 return nil
201 }
202
203
204 func prehashMsiDirent(item *comdoc.DirEnt, d io.Writer) {
205 buf := bytes.NewBuffer(make([]byte, 0, 128))
206 binary.Write(buf, binary.LittleEndian, item.RawDirEnt)
207 enc := buf.Bytes()
208
209 if item.Type != comdoc.DirRoot {
210 d.Write(enc[:item.NameLength-2])
211 }
212
213 if item.Type == comdoc.DirRoot || item.Type == comdoc.DirStorage {
214 d.Write(item.UID[:])
215 }
216
217 if item.Type == comdoc.DirStream {
218 d.Write(enc[120:124])
219 }
220
221 d.Write(enc[96:100])
222
223 if item.Type != comdoc.DirRoot {
224 d.Write(enc[100:116])
225 }
226 }
227
228
229 func sortMsiFiles(files []*comdoc.DirEnt) {
230 sort.Slice(files, func(i, j int) bool {
231 a, b := files[i], files[j]
232 n := a.NameLength
233 if b.NameLength < n {
234 n = b.NameLength
235 }
236
237 for k := uint16(0); k < n; k++ {
238 x, y := a.NameRunes[k], b.NameRunes[k]
239 x1, y1 := x&0xff, y&0xff
240 if x1 != y1 {
241 return x1 < y1
242 }
243 x2, y2 := x>>8, y>>8
244 if x2 != y2 {
245 return x2 < y2
246 }
247 }
248 return a.NameLength > b.NameLength
249 })
250 }
251
View as plain text