...

Source file src/github.com/sassoftware/relic/lib/authenticode/catalog.go

Documentation: github.com/sassoftware/relic/lib/authenticode

     1  //
     2  // Copyright (c) SAS Institute Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    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  		// this supposed to always be empty?
   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  	// The tag is a UTF-16-LE encoding of the hex of the imprint
   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