1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package authenticode
18
19 import (
20 "context"
21 "crypto"
22 "crypto/x509/pkix"
23 "encoding/asn1"
24 "encoding/binary"
25 "encoding/hex"
26 "errors"
27 "time"
28 "unicode/utf16"
29
30 "github.com/sassoftware/relic/lib/certloader"
31 "github.com/sassoftware/relic/lib/pkcs7"
32 "github.com/sassoftware/relic/lib/pkcs9"
33 "github.com/sassoftware/relic/lib/x509tools"
34 uuid "github.com/satori/go.uuid"
35 )
36
37 type Catalog struct {
38 Version int
39 Hash crypto.Hash
40 Sha1Entries, Sha2Entries []CertTrustEntry
41 }
42
43 func NewCatalog(hash crypto.Hash) *Catalog {
44 if hash == crypto.SHA1 {
45 return &Catalog{Version: 1, Hash: hash}
46 }
47 return &Catalog{Version: 2, Hash: hash}
48 }
49
50 func (cat *Catalog) makeCatalog() CertTrustList {
51 memberOid := OidCatalogListMember
52 if cat.Version == 2 {
53 memberOid = OidCatalogListMemberV2
54 }
55 return CertTrustList{
56 SubjectUsage: []asn1.ObjectIdentifier{OidCatalogList},
57 ListIdentifier: uuid.NewV4().Bytes(),
58 EffectiveDate: time.Now().UTC(),
59 SubjectAlgorithm: pkix.AlgorithmIdentifier{Algorithm: memberOid, Parameters: asn1.NullRawValue},
60 Entries: append(cat.Sha2Entries, cat.Sha1Entries...),
61 }
62 }
63
64 func (cat *Catalog) Marshal() ([]byte, error) {
65 return asn1.Marshal(cat.makeCatalog())
66 }
67
68 func (cat *Catalog) Sign(ctx context.Context, cert *certloader.Certificate) (*pkcs9.TimestampedSignature, error) {
69 sig := pkcs7.NewBuilder(cert.Signer(), cert.Chain(), cat.Hash)
70 if err := sig.SetContent(OidCertTrustList, cat.makeCatalog()); err != nil {
71 return nil, err
72 }
73 if err := addOpusAttrs(sig); err != nil {
74 return nil, err
75 }
76 psd, err := sig.Sign()
77 if err != nil {
78 return nil, err
79 }
80 return pkcs9.TimestampAndMarshal(ctx, psd, cert.Timestamper, true)
81 }
82
83 func (cat *Catalog) Add(indirect SpcIndirectDataContentPe) error {
84 sha2 := !indirect.MessageDigest.DigestAlgorithm.Algorithm.Equal(x509tools.OidDigestSHA1)
85 if sha2 && cat.Version == 1 {
86 return errors.New("can't add SHA2 digest to v1 catalog")
87 }
88 indirectBytes, err := asn1.Marshal(indirect)
89 if err != nil {
90 return err
91 }
92 indirectEntry := CertTrustValue{Attribute: OidSpcIndirectDataContent, Value: makeSet(indirectBytes)}
93 value := indirect.MessageDigest.Digest
94 if cat.Version == 1 {
95 memberInfo := CertTrustMemberInfoV1{
96 ClassID: x509tools.ToBMPString(CryptSipCreateIndirectData),
97 Unknown1: 512,
98 }
99 memberInfoEnc, err := asn1.Marshal(memberInfo)
100 if err != nil {
101 return err
102 }
103 catValue := CertTrustValue{Attribute: OidCatalogMemberInfo, Value: makeSet(memberInfoEnc)}
104 cat.Sha1Entries = append(cat.Sha1Entries, CertTrustEntry{
105 Tag: tagV1(value),
106 Values: []CertTrustValue{indirectEntry, catValue},
107 })
108 } else {
109
110 memberInfoEnc := []byte{0x80, 0}
111 catValue := CertTrustValue{Attribute: OidCatalogMemberInfoV2, Value: makeSet(memberInfoEnc)}
112 if sha2 {
113 cat.Sha2Entries = append(cat.Sha2Entries, CertTrustEntry{
114 Tag: value,
115 Values: []CertTrustValue{catValue, indirectEntry},
116 })
117 } else {
118 cat.Sha1Entries = append(cat.Sha1Entries, CertTrustEntry{
119 Tag: value,
120 Values: []CertTrustValue{catValue},
121 })
122 }
123 }
124 return nil
125 }
126
127 func tagV1(value []byte) []byte {
128
129 runes := utf16.Encode([]rune(hex.EncodeToString(value)))
130 tag := make([]byte, 2*len(runes))
131 for i, r := range runes {
132 binary.LittleEndian.PutUint16(tag[i*2:], r)
133 }
134 return tag
135 }
136
137 func makeSet(contents []byte) asn1.RawValue {
138 return asn1.RawValue{Tag: asn1.TagSet, IsCompound: true, Bytes: contents}
139 }
140
View as plain text