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/asn1"
24 "encoding/binary"
25 "errors"
26 "fmt"
27 "io"
28 "io/ioutil"
29
30 "github.com/sassoftware/relic/lib/pkcs7"
31 "github.com/sassoftware/relic/lib/pkcs9"
32 "github.com/sassoftware/relic/lib/x509tools"
33 "github.com/sassoftware/relic/signers/sigerrors"
34 )
35
36 type PESignature struct {
37 pkcs9.TimestampedSignature
38 Indirect *SpcIndirectDataContentPe
39 ImageHashFunc crypto.Hash
40 PageHashes []byte
41 PageHashFunc crypto.Hash
42 }
43
44
45 func VerifyPE(r io.ReadSeeker, skipDigests bool) ([]PESignature, error) {
46 hvals, err := findSignatures(r)
47 if err != nil {
48 return nil, err
49 } else if hvals.certSize == 0 {
50 return nil, sigerrors.NotSignedError{Type: "PECOFF"}
51 }
52
53 sigblob := make([]byte, hvals.certSize)
54 r.Seek(hvals.certStart, 0)
55 if _, err := io.ReadFull(r, sigblob); err != nil {
56 return nil, err
57 }
58
59 if skipDigests {
60 r = nil
61 }
62 return checkSignatures(sigblob, r)
63 }
64
65 func findSignatures(r io.ReadSeeker) (*peHeaderValues, error) {
66 if _, err := r.Seek(0, 0); err != nil {
67 return nil, err
68 }
69 d := ioutil.Discard
70 peStart, err := readDosHeader(r, d)
71 if err != nil {
72 return nil, err
73 }
74 r.Seek(peStart, 0)
75 fh, err := readCoffHeader(r, d)
76 if err != nil {
77 return nil, err
78 }
79 return readOptHeader(r, d, peStart, fh)
80 }
81
82 func checkSignatures(blob []byte, image io.ReadSeeker) ([]PESignature, error) {
83 values := make(map[crypto.Hash][]byte)
84 phvalues := make(map[crypto.Hash][]byte)
85 allhashes := make(map[crypto.Hash]bool)
86 sigs := make([]PESignature, 0, 1)
87 for len(blob) != 0 {
88 wLen := binary.LittleEndian.Uint32(blob[:4])
89 end := (int(wLen) + 7) / 8 * 8
90 size := int(wLen) - 8
91 if end > len(blob) || size < 0 {
92 return nil, errors.New("invalid certificate table")
93 }
94 cert := blob[8 : 8+size]
95 blob = blob[end:]
96
97 sig, err := checkSignature(cert)
98 if err != nil {
99 return nil, err
100 }
101 allhashes[sig.ImageHashFunc] = true
102 if len(sig.PageHashes) > 0 {
103 phvalues[sig.PageHashFunc] = sig.PageHashes
104 allhashes[sig.PageHashFunc] = true
105 }
106 sigs = append(sigs, *sig)
107 imageDigest := sig.Indirect.MessageDigest.Digest
108 if existing := values[sig.ImageHashFunc]; existing == nil {
109 values[sig.ImageHashFunc] = imageDigest
110 } else if !hmac.Equal(imageDigest, existing) {
111
112 return nil, fmt.Errorf("digest mismatch: %x != %x", imageDigest, existing)
113 }
114 }
115 if image == nil {
116 return sigs, nil
117 }
118 for hash := range allhashes {
119 imagehash := values[hash]
120 pagehashes := phvalues[hash]
121 if _, err := image.Seek(0, 0); err != nil {
122 return nil, err
123 }
124 doPageHashes := len(pagehashes) > 0
125 digest, err := DigestPE(image, hash, doPageHashes)
126 if err != nil {
127 return nil, err
128 }
129 if imagehash != nil && !hmac.Equal(digest.Imprint, imagehash) {
130 return nil, fmt.Errorf("digest mismatch: %x != %x", digest.Imprint, imagehash)
131 }
132 if pagehashes != nil && !hmac.Equal(digest.PageHashes, pagehashes) {
133 return nil, fmt.Errorf("page hash mismatch")
134 }
135 }
136 return sigs, nil
137 }
138
139 func checkSignature(der []byte) (*PESignature, error) {
140 psd, err := pkcs7.Unmarshal(der)
141 if err != nil {
142 return nil, err
143 }
144 if !psd.Content.ContentInfo.ContentType.Equal(OidSpcIndirectDataContent) {
145 return nil, errors.New("not an authenticode signature")
146 }
147 sig, err := psd.Content.Verify(nil, false)
148 if err != nil {
149 return nil, err
150 }
151 ts, err := pkcs9.VerifyOptionalTimestamp(sig)
152 if err != nil {
153 return nil, err
154 }
155 indirect := new(SpcIndirectDataContentPe)
156 if err := psd.Content.ContentInfo.Unmarshal(indirect); err != nil {
157 return nil, err
158 }
159 hash, err := x509tools.PkixDigestToHashE(indirect.MessageDigest.DigestAlgorithm)
160 if err != nil {
161 return nil, err
162 }
163 pesig := &PESignature{
164 TimestampedSignature: ts,
165 Indirect: indirect,
166 ImageHashFunc: hash,
167 }
168 if err := readPageHashes(pesig); err != nil {
169 return nil, err
170 }
171 return pesig, nil
172 }
173
174 func readPageHashes(sig *PESignature) error {
175 serObj := sig.Indirect.Data.Value.File.Moniker
176 if !bytes.Equal(serObj.ClassID, SpcUUIDPageHashes) {
177
178 return nil
179 }
180
181 var attrRaw asn1.RawValue
182 if _, err := asn1.Unmarshal(serObj.SerializedData, &attrRaw); err != nil {
183 return err
184 }
185 var attr SpcAttributePageHashes
186 if _, err := asn1.Unmarshal(attrRaw.Bytes, &attr); err != nil {
187 return err
188 }
189 switch {
190 case attr.Type.Equal(OidSpcPageHashV1):
191 sig.PageHashFunc = crypto.SHA1
192 case attr.Type.Equal(OidSpcPageHashV2):
193 sig.PageHashFunc = crypto.SHA256
194 default:
195 return errors.New("unknown page hash format")
196 }
197
198 sig.PageHashes = attr.Hashes[0]
199 if len(sig.PageHashes) == 0 || len(sig.PageHashes)%(4+sig.PageHashFunc.Size()) != 0 {
200 return errors.New("malformed page hash")
201 }
202 return nil
203 }
204
View as plain text