...

Source file src/github.com/sassoftware/relic/signers/apk/merkle.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  	"encoding/binary"
    23  
    24  	"github.com/pkg/errors"
    25  	"github.com/sassoftware/relic/lib/zipslicer"
    26  )
    27  
    28  const merkleBlock = 1048576
    29  
    30  // compute hashes of each 1MiB written
    31  type merkleHasher struct {
    32  	hashes []crypto.Hash
    33  	blocks [][]byte
    34  	buf    []byte
    35  	n      int
    36  	count  uint32
    37  }
    38  
    39  func newMerkleHasher(hashes []crypto.Hash) *merkleHasher {
    40  	return &merkleHasher{
    41  		buf:    make([]byte, merkleBlock),
    42  		hashes: hashes,
    43  		blocks: make([][]byte, len(hashes)),
    44  	}
    45  }
    46  
    47  func (h *merkleHasher) block(block []byte) {
    48  	var pref [5]byte
    49  	pref[0] = 0xa5
    50  	binary.LittleEndian.PutUint32(pref[1:], uint32(len(block)))
    51  	for i, hash := range h.hashes {
    52  		d := hash.New()
    53  		d.Write(pref[:])
    54  		d.Write(block)
    55  		h.blocks[i] = d.Sum(h.blocks[i])
    56  	}
    57  	h.count++
    58  }
    59  
    60  func (h *merkleHasher) Write(d []byte) (int, error) {
    61  	w := len(d)
    62  	// completing previously buffered data
    63  	if h.n != 0 && h.n+len(d) >= merkleBlock {
    64  		n := h.n
    65  		copy(h.buf[n:merkleBlock], d)
    66  		d = d[merkleBlock-n:]
    67  		h.block(h.buf)
    68  		h.n = 0
    69  	}
    70  	// larger than a block -- hash it directly
    71  	for len(d) >= merkleBlock {
    72  		h.block(d[:merkleBlock])
    73  		d = d[merkleBlock:]
    74  	}
    75  	// save the rest for later
    76  	if len(d) != 0 {
    77  		copy(h.buf[h.n:], d)
    78  		h.n += len(d)
    79  	}
    80  	return w, nil
    81  }
    82  
    83  func (h *merkleHasher) flush() {
    84  	if h.n != 0 {
    85  		h.block(h.buf[:h.n])
    86  		h.n = 0
    87  	}
    88  }
    89  
    90  // after content is written, finish the digest by adding the central directory and end of directory
    91  func (h *merkleHasher) Finish(inz *zipslicer.Directory, modified bool) ([][]byte, error) {
    92  	// https://source.android.com/security/apksigning/v2#integrity-protected-contents
    93  	// section 1: contents of zip entries (already written to hasher)
    94  	h.flush()
    95  	// section 2 is the signature block itself (not digested obviously)
    96  	// section 3: central directory
    97  	// TODO: zip64 support? android doesn't support it
    98  	if inz.DirLoc >= (1 << 32) {
    99  		return nil, errors.New("ZIP64 is not yet supported")
   100  	}
   101  	var cdirEntries, endOfDir []byte
   102  	if modified {
   103  		var b1, b2 bytes.Buffer
   104  		if err := inz.WriteDirectory(&b1, &b2, false); err != nil {
   105  			return nil, err
   106  		}
   107  		cdirEntries = b1.Bytes()
   108  		endOfDir = b2.Bytes()
   109  	} else {
   110  		var err error
   111  		cdirEntries, endOfDir, err = inz.GetOriginalDirectory(true)
   112  		if err != nil {
   113  			return nil, err
   114  		}
   115  	}
   116  	h.Write(cdirEntries)
   117  	h.flush()
   118  	// section 4: end of central directory
   119  	h.Write(endOfDir)
   120  	h.flush()
   121  	// compute final hash
   122  	var pref [5]byte
   123  	pref[0] = 0x5a
   124  	binary.LittleEndian.PutUint32(pref[1:], h.count)
   125  	ret := make([][]byte, len(h.hashes))
   126  	for i, hash := range h.hashes {
   127  		master := hash.New()
   128  		master.Write(pref[:])
   129  		master.Write(h.blocks[i])
   130  		ret[i] = master.Sum(nil)
   131  	}
   132  	return ret, nil
   133  }
   134  

View as plain text