1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package appmanifest
18
19 import (
20 "bytes"
21 "crypto"
22 "encoding/base64"
23 "encoding/hex"
24 "errors"
25 "fmt"
26
27 "github.com/beevik/etree"
28 "github.com/sassoftware/relic/lib/certloader"
29 "github.com/sassoftware/relic/lib/pkcs7"
30 "github.com/sassoftware/relic/lib/pkcs9"
31 "github.com/sassoftware/relic/lib/xmldsig"
32 )
33
34 const (
35 NsMsRel = "http://schemas.microsoft.com/windows/rel/2005/reldata"
36 NsMpeg21 = "urn:mpeg:mpeg21:2003:01-REL-R-NS"
37 NsAuthenticode = "http://schemas.microsoft.com/windows/pki/2005/Authenticode"
38 )
39
40 type SignedManifest struct {
41 ManifestSignature
42 Signed []byte
43 EncryptedDigest []byte
44 }
45
46
47 func Sign(manifest []byte, cert *certloader.Certificate, opts crypto.SignerOpts) (*SignedManifest, error) {
48 doc := etree.NewDocument()
49 if err := doc.ReadFromString(string(manifest)); err != nil {
50 return nil, err
51 }
52 root := doc.Root()
53
54 asi, err := setAssemblyIdentity(root, cert)
55 if err != nil {
56 return nil, err
57 }
58 subjectName, err := setPublisherIdentity(root, cert)
59 if err != nil {
60 return nil, err
61 }
62
63 sigopts := xmldsig.SignOptions{MsCompatHashNames: true, IncludeKeyValue: true}
64 if err := xmldsig.Sign(root, root, opts.HashFunc(), cert.Signer(), cert.Chain(), sigopts); err != nil {
65 return nil, err
66 }
67 sig, keyinfo := setSigIds(root, "StrongNameSignature", "StrongNameKeyInfo")
68
69 manifestHash := makeManifestHash(sig)
70 license, sigDestNode := makeLicense(asi, subjectName, manifestHash)
71
72 sigopts.IncludeX509 = true
73 if err := xmldsig.Sign(license, sigDestNode, opts.HashFunc(), cert.Signer(), cert.Chain(), sigopts); err != nil {
74 return nil, err
75 }
76 aSig, _ := setSigIds(sigDestNode, "AuthenticodeSignature", "")
77
78 license.AddChild(sigDestNode)
79 reldata := keyinfo.CreateElement("msrel:RelData")
80 reldata.CreateAttr("xmlns:msrel", NsMsRel)
81 reldata.AddChild(license)
82
83 signed, err := doc.WriteToBytes()
84 if err != nil {
85 return nil, err
86 }
87
88 encryptedDigest, _ := base64.StdEncoding.DecodeString(aSig.SelectElement("SignatureValue").Text())
89 return &SignedManifest{
90 Signed: signed,
91 EncryptedDigest: encryptedDigest,
92 ManifestSignature: ManifestSignature{
93 Signature: &pkcs9.TimestampedSignature{Signature: pkcs7.Signature{
94 Certificate: cert.Leaf,
95 Intermediates: cert.Chain(),
96 }},
97 AssemblyName: asi.SelectAttrValue("name", ""),
98 AssemblyVersion: asi.SelectAttrValue("version", ""),
99 Hash: opts.HashFunc(),
100 PublicKeyToken: asi.SelectAttrValue("publicKeyToken", ""),
101 }}, nil
102 }
103
104
105
106
107 func setAssemblyIdentity(root *etree.Element, cert *certloader.Certificate) (*etree.Element, error) {
108 token, err := PublicKeyToken(cert.Leaf.PublicKey)
109 if err != nil {
110 return nil, err
111 }
112 asi := root.SelectElement("assemblyIdentity")
113 if asi == nil {
114 return nil, errors.New("manifest has no top-level assemblyIdentity element")
115 }
116 asi.CreateAttr("publicKeyToken", token)
117 return asi, nil
118 }
119
120
121 func setPublisherIdentity(root *etree.Element, cert *certloader.Certificate) (string, error) {
122
123 subjectName, issuerKeyHash, err := PublisherIdentity(cert)
124 if err != nil {
125 return "", err
126 }
127 xmldsig.RemoveElements(root, "publisherIdentity")
128 ident := root.CreateElement("publisherIdentity")
129 ident.CreateAttr("name", subjectName)
130 ident.CreateAttr("issuerKeyHash", issuerKeyHash)
131 return subjectName, nil
132 }
133
134
135 func makeLicense(asi *etree.Element, subjectName, manifestHash string) (*etree.Element, *etree.Element) {
136 license := etree.NewElement("r:license")
137 license.CreateAttr("xmlns:r", NsMpeg21)
138 license.CreateAttr("xmlns:as", NsAuthenticode)
139
140 grant := license.CreateElement("r:grant")
141 minfo := grant.CreateElement("as:ManifestInformation")
142 minfo.CreateAttr("Hash", manifestHash)
143 minfo.CreateAttr("Description", "")
144 minfo.CreateAttr("Url", "")
145 massy := asi.Copy()
146 massy.Space = "as"
147 minfo.AddChild(massy)
148 grant.CreateElement("as:SignedBy")
149 grant.CreateElement("as:AuthenticodePublisher").CreateElement("as:X509SubjectName").SetText(subjectName)
150
151 issuer := license.CreateElement("r:issuer")
152 return license, issuer
153 }
154
155
156
157 func makeManifestHash(sig *etree.Element) string {
158 dv := sig.FindElement(".//DigestValue")
159 blob, _ := base64.StdEncoding.DecodeString(dv.Text())
160 for i := 0; i < len(blob)/2; i++ {
161 j := len(blob) - i - 1
162 blob[i], blob[j] = blob[j], blob[i]
163 }
164 return hex.EncodeToString(blob)
165 }
166
167
168 func setSigIds(root *etree.Element, sigName, keyinfoName string) (sig, keyinfo *etree.Element) {
169 sig = root.SelectElement("Signature")
170 if sigName != "" {
171 sig.CreateAttr("Id", sigName)
172 }
173 keyinfo = sig.SelectElement("KeyInfo")
174 if keyinfoName != "" {
175 keyinfo.CreateAttr("Id", keyinfoName)
176 }
177 return sig, keyinfo
178 }
179
180
181 func (m *SignedManifest) AddTimestamp(token *pkcs7.ContentInfoSignedData) error {
182 doc := etree.NewDocument()
183 if err := doc.ReadFromString(string(m.Signed)); err != nil {
184 return err
185 }
186 aSig := doc.Root().FindElement("Signature/KeyInfo/msrel:RelData/r:license/r:issuer/Signature")
187 if aSig == nil {
188 return errors.New("manifest has no authenticode signature")
189 }
190 siblob, err := token.Marshal()
191 if err != nil {
192 return err
193 }
194 lines := (47 + len(siblob)) / 48
195 buf := bytes.NewBuffer(make([]byte, 0, (64+2)*lines))
196 for len(siblob) > 0 {
197 n := len(siblob)
198 if n > 48 {
199 n = 48
200 }
201 chunk := siblob[:n]
202 siblob = siblob[n:]
203
204 buf.WriteString(base64.StdEncoding.EncodeToString(chunk))
205 buf.WriteString("\r\n")
206 }
207 aSig.CreateElement("Object").CreateElement("as:Timestamp").SetText(buf.String())
208 signed, err := doc.WriteToBytes()
209 if err != nil {
210 return err
211 }
212 cs, err := VerifyTimestamp(token, m.EncryptedDigest, m.Signature.Intermediates)
213 if err != nil {
214 return fmt.Errorf("failed to validate timestamp: %s", err)
215 }
216 m.Signed = signed
217 m.Signature.CounterSignature = cs
218 return nil
219 }
220
View as plain text