...

Source file src/github.com/sassoftware/relic/lib/signjar/sign.go

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

     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 signjar
    18  
    19  import (
    20  	"archive/zip"
    21  	"bytes"
    22  	"context"
    23  	"crypto"
    24  	"crypto/ecdsa"
    25  	"crypto/rsa"
    26  	"crypto/x509"
    27  	"encoding/asn1"
    28  	"errors"
    29  	"path"
    30  	"strings"
    31  	"time"
    32  
    33  	"github.com/sassoftware/relic/lib/binpatch"
    34  	"github.com/sassoftware/relic/lib/certloader"
    35  	"github.com/sassoftware/relic/lib/pkcs7"
    36  	"github.com/sassoftware/relic/lib/pkcs9"
    37  	"github.com/sassoftware/relic/lib/zipslicer"
    38  )
    39  
    40  func (jd *JarDigest) Sign(ctx context.Context, cert *certloader.Certificate, alias string, sectionsOnly, inlineSignature, apkV2 bool) (*binpatch.PatchSet, *pkcs9.TimestampedSignature, error) {
    41  	// Create sigfile from the manifest
    42  	sf, err := DigestManifest(jd.Manifest, jd.Hash, sectionsOnly, apkV2)
    43  	if err != nil {
    44  		return nil, nil, err
    45  	}
    46  	// Sign sigfile
    47  	sig := pkcs7.NewBuilder(cert.Signer(), cert.Chain(), jd.Hash)
    48  	if err := sig.SetContentData(sf); err != nil {
    49  		return nil, nil, err
    50  	}
    51  	psd, err := sig.Sign()
    52  	if err != nil {
    53  		return nil, nil, err
    54  	}
    55  	ts, err := pkcs9.TimestampAndMarshal(ctx, psd, cert.Timestamper, false)
    56  	if err != nil {
    57  		return nil, nil, err
    58  	}
    59  	// Rebuild zip with updated manifest, sigfile, and signature
    60  	psig := ts.Raw
    61  	if !inlineSignature {
    62  		if _, err := psd.Detach(); err != nil {
    63  			return nil, nil, err
    64  		}
    65  		psig, err = asn1.Marshal(*psd)
    66  		if err != nil {
    67  			return nil, nil, err
    68  		}
    69  	}
    70  	patch, err := jd.insertSignature(cert.Leaf, alias, sf, psig)
    71  	if err != nil {
    72  		return nil, nil, err
    73  	}
    74  	return patch, ts, nil
    75  }
    76  
    77  func (jd *JarDigest) insertSignature(cert *x509.Certificate, alias string, sf, sig []byte) (*binpatch.PatchSet, error) {
    78  	signame, pkcsname := sigNames(cert.PublicKey, alias)
    79  	deflate := jd.shouldDeflate()
    80  	// Add new files to beginning of zip
    81  	outz := new(zipslicer.Directory)
    82  	var zipcon bytes.Buffer
    83  	mtime := time.Now()
    84  	if _, err := outz.NewFile(metaInf, jarMagic, nil, &zipcon, mtime, false, false); err != nil {
    85  		return nil, err
    86  	}
    87  	if _, err := outz.NewFile(manifestName, jarMagic, jd.Manifest, &zipcon, mtime, deflate, false); err != nil {
    88  		return nil, err
    89  	}
    90  	if _, err := outz.NewFile(metaInf+signame, nil, sf, &zipcon, mtime, deflate, false); err != nil {
    91  		return nil, err
    92  	}
    93  	if _, err := outz.NewFile(metaInf+pkcsname, nil, sig, &zipcon, mtime, deflate, false); err != nil {
    94  		return nil, err
    95  	}
    96  	// Patch out old files
    97  	patch := binpatch.New()
    98  	patch.Add(0, 0, zipcon.Bytes())
    99  	for _, f := range jd.inz.File {
   100  		if keepFile(f.Name) {
   101  			// Add existing file to the new zip directory. Its offset will be changed.
   102  			if _, err := outz.AddFile(f); err != nil {
   103  				return nil, err
   104  			}
   105  		} else {
   106  			// remove this region from the old zip
   107  			size, err := f.GetTotalSize()
   108  			if err != nil {
   109  				return nil, err
   110  			}
   111  			if size > 0xffffffff {
   112  				return nil, errors.New("signature file too big")
   113  			}
   114  			patch.Add(int64(f.Offset), size, nil)
   115  		}
   116  	}
   117  	zipdir := new(bytes.Buffer)
   118  	if err := outz.WriteDirectory(zipdir, zipdir, false); err != nil {
   119  		return nil, err
   120  	}
   121  	patch.Add(jd.inz.DirLoc, jd.inz.Size-jd.inz.DirLoc, zipdir.Bytes())
   122  	return patch, nil
   123  }
   124  
   125  // name for the signature file is based on the key type
   126  func sigNames(pubkey crypto.PublicKey, alias string) (signame, pkcsname string) {
   127  	signame = strings.ToUpper(alias) + ".SF"
   128  	pkcsname = strings.ToUpper(alias)
   129  	switch pubkey.(type) {
   130  	case *rsa.PublicKey:
   131  		pkcsname += ".RSA"
   132  	case *ecdsa.PublicKey:
   133  		pkcsname += ".EC"
   134  	default:
   135  		signame = "SIG-" + signame
   136  		pkcsname = "SIG-" + pkcsname + ".SIG"
   137  	}
   138  	return
   139  }
   140  
   141  // deflate if the original manifest was deflated
   142  func (jd *JarDigest) shouldDeflate() bool {
   143  	for _, f := range jd.inz.File {
   144  		if f.Name == manifestName {
   145  			return f.Method != zip.Store
   146  		}
   147  	}
   148  	return false
   149  }
   150  
   151  func keepFile(name string) bool {
   152  	if name == metaInf {
   153  		// META-INF/ itself gets updated
   154  		return false
   155  	}
   156  	if path.Dir(name)+"/" != metaInf {
   157  		// everything not an immediate child of META-INF/ is kept
   158  		return true
   159  	}
   160  	name = path.Base(name)
   161  	if strings.HasPrefix(name, "SIG-") {
   162  		// delete all old signatures
   163  		return false
   164  	}
   165  	switch path.Ext(name) {
   166  	case ".MF":
   167  		// replace the manifest
   168  		return false
   169  	case ".SF":
   170  		// delete all old signatures
   171  		return false
   172  	case ".RSA", ".DSA", ".EC", ".SIG":
   173  		return false
   174  	default:
   175  		// all other META-INF/ files are kept
   176  		return true
   177  	}
   178  }
   179  

View as plain text