...

Source file src/github.com/sassoftware/relic/signers/apk/verify.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  	"archive/zip"
    21  	"bytes"
    22  	"crypto"
    23  	"crypto/ecdsa"
    24  	"crypto/hmac"
    25  	"crypto/rsa"
    26  	"crypto/x509"
    27  	"encoding/binary"
    28  	"fmt"
    29  	"io"
    30  	"os"
    31  	"strings"
    32  
    33  	"github.com/pkg/errors"
    34  	"github.com/sassoftware/relic/lib/pkcs7"
    35  	"github.com/sassoftware/relic/lib/pkcs9"
    36  	"github.com/sassoftware/relic/lib/signjar"
    37  	"github.com/sassoftware/relic/lib/x509tools"
    38  	"github.com/sassoftware/relic/lib/zipslicer"
    39  	"github.com/sassoftware/relic/signers"
    40  	"github.com/sassoftware/relic/signers/sigerrors"
    41  )
    42  
    43  func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) {
    44  	// verify v2
    45  	inz, block, err := getSigBlock(f)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	var allSigs []*signers.Signature
    50  	for len(block) > 0 {
    51  		if len(block) < 12 {
    52  			return nil, errTruncated
    53  		}
    54  		partSize := binary.LittleEndian.Uint64(block)
    55  		block = block[8:]
    56  		if partSize < 4 || partSize > uint64(len(block)) {
    57  			return nil, errTruncated
    58  		}
    59  		partType := binary.LittleEndian.Uint32(block)
    60  		partBlob := block[4:partSize]
    61  		block = block[partSize:]
    62  		if partType != sigApkV2 {
    63  			continue
    64  		}
    65  		var signerList []apkSigner
    66  		if err := unmarshal(partBlob, &signerList); err != nil {
    67  			return nil, errors.Wrap(err, "parsing signature block")
    68  		} else if len(signerList) == 0 {
    69  			return nil, errors.New("empty APK signing block")
    70  		}
    71  		for i, signer := range signerList {
    72  			sig, err := signer.Verify(nil)
    73  			if err != nil {
    74  				return nil, errors.Wrapf(err, "APK signature #%d", i+1)
    75  			}
    76  			allSigs = append(allSigs, sig)
    77  		}
    78  	}
    79  	v2present := len(allSigs) != 0
    80  	// verify v1
    81  	inzr, err := zip.NewReader(f, inz.Size)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	jarSigs, err := signjar.Verify(inzr, false)
    86  	if err != nil {
    87  		if _, ok := err.(sigerrors.NotSignedError); !ok {
    88  			return nil, err
    89  		}
    90  	}
    91  	for _, jarSig := range jarSigs {
    92  		apk := jarSig.SignatureHeader.Get("X-Android-APK-Signed")
    93  		if strings.ContainsRune(apk, '2') && !v2present {
    94  			return nil, errors.New("V1 signature contains X-Android-APK-Signed header but no V2 signature exists")
    95  		}
    96  		allSigs = append(allSigs, &signers.Signature{
    97  			SigInfo:       "v1",
    98  			Hash:          jarSig.Hash,
    99  			X509Signature: &jarSig.TimestampedSignature,
   100  		})
   101  	}
   102  	if len(allSigs) == 0 {
   103  		return nil, sigerrors.NotSignedError{Type: "APK"}
   104  	}
   105  	return allSigs, nil
   106  }
   107  
   108  func getSigBlock(f *os.File) (*zipslicer.Directory, []byte, error) {
   109  	size, err := f.Seek(0, io.SeekEnd)
   110  	if err != nil {
   111  		return nil, nil, err
   112  	}
   113  	inz, err := zipslicer.Read(f, size)
   114  	if err != nil {
   115  		return nil, nil, err
   116  	}
   117  	// check that there is stuff between the last file and the start of the directory
   118  	if len(inz.File) == 0 {
   119  		return nil, nil, errors.New("no files in APK")
   120  	}
   121  	sigLoc, err := inz.NextFileOffset()
   122  	if err != nil {
   123  		return nil, nil, err
   124  	}
   125  	if sigLoc == inz.DirLoc {
   126  		// not signed
   127  		return inz, nil, nil
   128  	}
   129  	// read signature block
   130  	blob := make([]byte, inz.DirLoc-sigLoc)
   131  	if _, err := f.ReadAt(blob, sigLoc); err != nil {
   132  		return nil, nil, err
   133  	}
   134  	// check magic
   135  	if !bytes.HasSuffix(blob, []byte(sigMagic)) {
   136  		return nil, nil, errMalformed
   137  	}
   138  	expected := uint64(len(blob) - 8)
   139  	size1 := binary.LittleEndian.Uint64(blob)
   140  	size2 := binary.LittleEndian.Uint64(blob[len(blob)-24:])
   141  	if size1 != expected || size2 != expected {
   142  		return nil, nil, errMalformed
   143  	}
   144  	return inz, blob[8 : len(blob)-24], nil
   145  }
   146  
   147  func (s *apkSigner) Verify(inz *zipslicer.Directory) (*signers.Signature, error) {
   148  	if len(s.Signatures) == 0 {
   149  		return nil, errors.New("no signatures in APK signer block")
   150  	}
   151  	// check signatures over SignedData
   152  	publicKey, err := x509.ParsePKIXPublicKey(s.PublicKey)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	var bestHash crypto.Hash
   157  	for _, sig := range s.Signatures {
   158  		hash, err := sig.VerifySignature(publicKey, s.SignedData.Bytes())
   159  		if err != nil {
   160  			return nil, err
   161  		}
   162  		// check them all but only need to report one per signature
   163  		if hash > bestHash {
   164  			bestHash = hash
   165  		}
   166  	}
   167  	// check digests
   168  	var signedData apkSignedData
   169  	if err := unmarshal(s.SignedData, &signedData); err != nil {
   170  		return nil, err
   171  	}
   172  	if len(signedData.Digests) == 0 {
   173  		return nil, errors.New("no digests in APK signed data block")
   174  	}
   175  	if inz != nil {
   176  		hashes := make([]crypto.Hash, len(signedData.Digests))
   177  		for i, digest := range signedData.Digests {
   178  			st, err := sigTypeByID(digest.ID)
   179  			if err != nil {
   180  				return nil, err
   181  			}
   182  			hashes[i] = st.hash
   183  		}
   184  		hasher := newMerkleHasher(hashes)
   185  		for _, f := range inz.File {
   186  			if _, err := f.Dump(hasher); err != nil {
   187  				return nil, err
   188  			}
   189  		}
   190  		digests, err := hasher.Finish(inz, false)
   191  		if err != nil {
   192  			return nil, err
   193  		}
   194  		for i, digest := range signedData.Digests {
   195  			if !hmac.Equal(digest.Value, digests[i]) {
   196  				return nil, fmt.Errorf("digest mismatch for algorithm 0x%04x", digest.ID)
   197  			}
   198  		}
   199  	}
   200  	// identify which certificate is the leaf
   201  	certs, err := signedData.ParseCertificates()
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  	var leaf *x509.Certificate
   206  	var intermediates []*x509.Certificate
   207  	for _, cert := range certs {
   208  		if bytes.Equal(cert.RawSubjectPublicKeyInfo, s.PublicKey) {
   209  			leaf = cert
   210  		} else {
   211  			intermediates = append(intermediates, cert)
   212  		}
   213  	}
   214  	if leaf == nil {
   215  		return nil, errors.New("public key does not match any certificate")
   216  	}
   217  	return &signers.Signature{
   218  		SigInfo: "v2",
   219  		Hash:    bestHash,
   220  		X509Signature: &pkcs9.TimestampedSignature{
   221  			Signature: pkcs7.Signature{
   222  				Certificate:   leaf,
   223  				Intermediates: intermediates,
   224  			},
   225  		},
   226  	}, nil
   227  }
   228  
   229  func (sig *apkSignature) VerifySignature(publicKey interface{}, signedData []byte) (crypto.Hash, error) {
   230  	st, err := sigTypeByID(sig.ID)
   231  	if err != nil {
   232  		return 0, err
   233  	}
   234  	d := st.hash.New()
   235  	d.Write(signedData)
   236  	hashed := d.Sum(nil)
   237  	switch st.alg {
   238  	case x509.RSA:
   239  		pub, ok := publicKey.(*rsa.PublicKey)
   240  		if !ok {
   241  			return 0, errors.New("public key algorithm mismatch")
   242  		}
   243  		if st.pss {
   244  			err = rsa.VerifyPSS(pub, st.hash, hashed, sig.Value, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash})
   245  		} else {
   246  			err = rsa.VerifyPKCS1v15(pub, st.hash, hashed, sig.Value)
   247  		}
   248  		if err != nil {
   249  			return 0, err
   250  		}
   251  	case x509.ECDSA:
   252  		pub, ok := publicKey.(*ecdsa.PublicKey)
   253  		if !ok {
   254  			return 0, errors.New("public key algorithm mismatch")
   255  		}
   256  		esig, err := x509tools.UnmarshalEcdsaSignature(sig.Value)
   257  		if err != nil {
   258  			return 0, err
   259  		}
   260  		if !ecdsa.Verify(pub, hashed, esig.R, esig.S) {
   261  			return 0, errors.New("ECDSA verification failed")
   262  		}
   263  	default:
   264  		return 0, errors.New("unsupported public key algorithm")
   265  	}
   266  	return st.hash, nil
   267  }
   268  

View as plain text