...

Source file src/github.com/sassoftware/relic/signers/apk/digest.go

Documentation: github.com/sassoftware/relic/signers/apk

     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 apk
    18  
    19  import (
    20  	"bytes"
    21  	"crypto"
    22  	"crypto/rand"
    23  	"encoding/binary"
    24  	"io"
    25  
    26  	"github.com/pkg/errors"
    27  	"github.com/sassoftware/relic/lib/binpatch"
    28  	"github.com/sassoftware/relic/lib/certloader"
    29  	"github.com/sassoftware/relic/lib/x509tools"
    30  	"github.com/sassoftware/relic/lib/zipslicer"
    31  )
    32  
    33  type Digest struct {
    34  	inz    *zipslicer.Directory
    35  	hash   crypto.Hash
    36  	value  []byte
    37  	sigLoc int64
    38  }
    39  
    40  func digestApkStream(r io.Reader, hash crypto.Hash) (*Digest, error) {
    41  	inz, err := zipslicer.ReadZipTar(r)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	hasher := newMerkleHasher([]crypto.Hash{hash})
    46  	for _, f := range inz.File {
    47  		_, err := f.Dump(hasher)
    48  		if err != nil {
    49  			return nil, err
    50  		}
    51  	}
    52  	sigLoc, err := inz.NextFileOffset()
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	origDirLoc := inz.DirLoc
    57  	inz.DirLoc = sigLoc
    58  	digests, err := hasher.Finish(inz, true)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	inz.DirLoc = origDirLoc
    63  	return &Digest{
    64  		inz:    inz,
    65  		hash:   hash,
    66  		value:  digests[0],
    67  		sigLoc: sigLoc,
    68  	}, nil
    69  }
    70  
    71  func (d *Digest) Sign(cert *certloader.Certificate) (*binpatch.PatchSet, error) {
    72  	// select a signature type
    73  	alg := x509tools.GetPublicKeyAlgorithm(cert.Leaf.PublicKey)
    74  	var st sigType
    75  	for _, s := range sigTypes {
    76  		if s.hash == d.hash && s.alg == alg && !s.pss {
    77  			st = s
    78  			break
    79  		}
    80  		// TODO: PSS
    81  	}
    82  	if st.id == 0 {
    83  		return nil, errors.New("unsupported public key algorithm")
    84  	}
    85  	// build signed data
    86  	sd := apkSignedData{
    87  		Digests: []apkDigest{apkDigest{ID: st.id, Value: d.value}},
    88  	}
    89  	for _, cert := range cert.Chain() {
    90  		sd.Certificates = append(sd.Certificates, cert.Raw)
    91  	}
    92  	signedData, err := marshal(sd)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  	// sign
    97  	digest := st.hash.New()
    98  	digest.Write(signedData.Bytes())
    99  	sigv, err := cert.Signer().Sign(rand.Reader, digest.Sum(nil), st.hash)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	// build signer block
   104  	signerList := []apkSigner{apkSigner{
   105  		SignedData: signedData,
   106  		Signatures: []apkSignature{apkSignature{ID: st.id, Value: sigv}},
   107  		PublicKey:  cert.Leaf.RawSubjectPublicKeyInfo,
   108  	}}
   109  	sblob, err := marshal(signerList)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	block := makeSigBlock(sblob)
   114  	// patch
   115  	patchset := binpatch.New()
   116  	origDirLoc := d.inz.DirLoc
   117  	patchset.Add(d.sigLoc, origDirLoc-d.sigLoc, block)
   118  	d.inz.DirLoc = d.sigLoc + int64(len(block))
   119  	var dirEnts, endOfDir bytes.Buffer
   120  	if err := d.inz.WriteDirectory(&dirEnts, &endOfDir, false); err != nil {
   121  		return nil, err
   122  	}
   123  	patchset.Add(origDirLoc+int64(dirEnts.Len()), int64(endOfDir.Len()), endOfDir.Bytes())
   124  	return patchset, nil
   125  }
   126  
   127  func makeSigBlock(sblob []byte) []byte {
   128  	block := make([]byte, 8+12+len(sblob)+24)
   129  	// length prefix on signing block, includes the magic suffix but not itself
   130  	binary.LittleEndian.PutUint64(block, uint64(8+4+len(sblob)+8+16))
   131  	// length prefix on the inner block
   132  	binary.LittleEndian.PutUint64(block[8:], uint64(4+len(sblob)))
   133  	// block type
   134  	binary.LittleEndian.PutUint32(block[8+8:], sigApkV2)
   135  	// the block itself
   136  	copy(block[8+8+4:], sblob)
   137  	// magic suffix
   138  	suffix := block[8+8+4+len(sblob):]
   139  	copy(suffix, block[:8])    // length again
   140  	copy(suffix[8:], sigMagic) // magic
   141  	return block
   142  }
   143  

View as plain text