1 Star 0 Fork 0

奈蜇579 / apkverifier

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
signingblock.go 20.70 KB
一键复制 编辑 原始数据 按行查看 历史
Vojtěch Boček 提交于 2023-10-31 12:33 . feat: update to Android 14
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
package signingblock
import (
"bytes"
"crypto/sha256"
"crypto/x509"
"encoding/binary"
"errors"
"fmt"
"hash"
"io"
"math"
"os"
"github.com/avast/apkparser"
"github.com/avast/apkverifier/apilevel"
)
// https://source.android.com/security/apksigning/v2.html
// frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
type BlockId uint32
const (
// BlockIdDependencyMetadata Dependencies metadata generated by Gradle and encrypted by Google Play.
// "...The data is compressed, encrypted by a Google Play signing key..."
// https://developer.android.com/studio/releases/gradle-plugin#dependency-metadata
BlockIdDependencyMetadata BlockId = 0x504b4453
// BlockIdMeituanMetadata JSON with some metadata, used by Chinese company Meituan
BlockIdMeituanMetadata BlockId = 0x71777777
// BlockIdSourceStampV1 Older SourceStamp implementation, you should not encounter this ID
// https://android.googlesource.com/platform/frameworks/base/+/549ce7a482ed4fe170ca445324fb38c447030404%5E%21/#F0
BlockIdSourceStampV1 BlockId = 0x2b09189e
blockIdVerityPadding BlockId = 0x42726577
blockIdSchemeV2 BlockId = 0x7109871a
blockIdSchemeV3 BlockId = 0xf05368c0
blockIdSchemeV31 BlockId = 0x1b93ad61
blockIdFrosting BlockId = 0x2146444e
blockIdSourceStampV2 BlockId = 0x6dff800d
)
func (b BlockId) String() string {
switch b {
case BlockIdDependencyMetadata:
return "DependencyMetadata"
case BlockIdMeituanMetadata:
return "MeituanMetadata"
case BlockIdSourceStampV1:
return "SourceStampV1 (deprecated)"
case blockIdSourceStampV2:
return "SourceStampV2"
case blockIdVerityPadding:
return "VerityPadding"
case blockIdSchemeV2:
return "SchemeV2Signature"
case blockIdSchemeV3:
return "SchemeV3Signature"
case blockIdSchemeV31:
return "SchemeV3.1Signature"
case blockIdFrosting:
return "PlayFrosting"
default:
return fmt.Sprintf("Unknown (0x%08x)", uint32(b))
}
}
const (
eocdRecMinSize = 22
eocdRecMagic = 0x06054b50
eocdCommentSizeOffset = 20
eocdCentralDirSizeOffset = 12
eocdCentralDirOffsetOffset = 16
zip64LocatorSize = 20
zip64LocatorMagic = 0x07064b50
apkSigBlockMinSize = 32
apkSigBlockMagicHi = 0x3234206b636f6c42
apkSigBlockMagicLo = 0x20676953204b5041
maxChunkSize = 1024 * 1024
maxApkSigners = 10
schemeIdV1 = 1
schemeIdV2 = 2
schemeIdV3 = 3
schemeIdV31 = 31
)
var schemeIdToBlockId = map[int32]BlockId{
schemeIdV2: blockIdSchemeV2,
schemeIdV3: blockIdSchemeV3,
schemeIdV31: blockIdSchemeV31,
}
var (
errNoSigningBlockSignature = errors.New("This apk does not have signing block signature")
errEocdNotFound = errors.New("EOCD record not found.")
)
type signatureBlockScheme interface {
parseSigners(block *bytes.Buffer, contentDigests map[contentDigest][]byte, result *VerificationResult)
finalizeResult(minSdkVersion, maxSdkVersion int32, result *VerificationResult)
}
type signingBlock struct {
file io.ReadSeeker
fileSize int64
eocdOffset int64
centralDirOffset int64
centralDirSize int64
sigBlockOffset int64
eocd []byte
}
type signingBlockNotFoundError struct {
err error
}
func (e *signingBlockNotFoundError) Error() string {
return "Signature Block signature not found: " + e.err.Error()
}
func IsSigningBlockNotFoundError(err error) bool {
_, ok := err.(*signingBlockNotFoundError)
return ok
}
func VerifySigningBlock(path string, minSdkVersion, maxSdkVersion int32) (res *VerificationResult, magic uint32, err error) {
f, err := os.Open(path)
if err != nil {
return
}
defer f.Close()
return VerifySigningBlockReader(f, minSdkVersion, maxSdkVersion)
}
func VerifySigningBlockReaderWithZip(r io.ReadSeeker, minSdkVersion, maxSdkVersion int32, optionalZip *apkparser.ZipReader) (res *VerificationResult, magic uint32, err error) {
if optionalZip == nil {
optionalZip, err = apkparser.OpenZipReader(r)
if err != nil {
return
}
defer optionalZip.Close()
}
var s *signingBlock
s, magic, err = newSigningBlock(r)
if err != nil {
return
}
res = &VerificationResult{}
blocks, frosting, err := s.findSignatureBlocks(maxSdkVersion, res)
if frosting != nil && res.Frosting.Error == nil {
v2block := blocks[blockIdSchemeV2]
if v2block == nil && len(res.ExtraBlocks) != 0 {
v2block = res.ExtraBlocks[blockIdSchemeV2]
}
res.Frosting.Error = frosting.verifyApk(r, s.sigBlockOffset, v2block, s.centralDirOffset, s.centralDirSize, s.eocd)
}
if err != nil {
err = &signingBlockNotFoundError{err}
return
}
contentDigests := s.pickAndVerify(blocks, minSdkVersion, maxSdkVersion, res)
// On levels < 28, we can fallback to v2 - v3 is not required to be valid.
// 1e2ab0af91dce1be16525d9d6d6e6d645788ea627edc64cb9cd379b35e01f53f
if res.ContainsErrors() && res.SchemeId >= schemeIdV3 && minSdkVersion < apilevel.V9_0_Pie && blocks[blockIdSchemeV2] != nil {
if res.ExtraBlocks == nil {
res.ExtraBlocks = make(map[BlockId][]byte)
}
res.ExtraBlocks[blockIdSchemeV3] = blocks[blockIdSchemeV3]
delete(blocks, blockIdSchemeV3)
for _, e := range res.Errors {
res.Warnings = append(res.Warnings, fmt.Sprintf("schemeV3: %s", e.Error()))
}
res.Errors = nil
contentDigests = s.pickAndVerify(blocks, minSdkVersion, maxSdkVersion, res)
}
stampVerifier := sourceStampVerifier{
verifiedSchemeId: int32(res.SchemeId),
minSdkVersion: minSdkVersion,
maxSdkVersion: maxSdkVersion,
}
res.SourceStamp = stampVerifier.VerifySourceV2Stamp(optionalZip, blocks[blockIdSourceStampV2], contentDigests)
err = res.GetLastError()
return
}
func (s *signingBlock) pickAndVerify(blocks map[BlockId][]byte, minSdkVersion, maxSdkVersion int32, res *VerificationResult) map[contentDigest][]byte {
if !apilevel.SupportsSigV2(maxSdkVersion) {
res.SchemeId = 1
return nil
}
var block []byte
var err error
var scheme signatureBlockScheme
res.SchemeId, scheme, block, err = s.pickScheme(blocks, minSdkVersion, maxSdkVersion)
if err != nil {
res.Errors = append(res.Errors, err)
return nil
}
return s.verify(scheme, block, minSdkVersion, maxSdkVersion, res)
}
func VerifySigningBlockReader(r io.ReadSeeker, minSdkVersion, maxSdkVersion int32) (res *VerificationResult, magic uint32, err error) {
return VerifySigningBlockReaderWithZip(r, minSdkVersion, maxSdkVersion, nil)
}
func ExtractCerts(path string, minSdkVersion, maxSdkVersion int32) (certs [][]*x509.Certificate, err error) {
f, err := os.Open(path)
if err != nil {
return
}
defer f.Close()
return ExtractCertsReader(f, minSdkVersion, maxSdkVersion)
}
func ExtractCertsReader(r io.ReadSeeker, minSdkVersion, maxSdkVersion int32) (certs [][]*x509.Certificate, err error) {
var s *signingBlock
s, _, err = newSigningBlock(r)
if err != nil {
return
}
res := &VerificationResult{}
blocks, _, err := s.findSignatureBlocks(maxSdkVersion, res)
if err != nil {
err = &signingBlockNotFoundError{err}
return
}
var block []byte
var scheme signatureBlockScheme
res.SchemeId, scheme, block, err = s.pickScheme(blocks, minSdkVersion, maxSdkVersion)
if err != nil {
return nil, err
}
s.extractCerts(scheme, block, res)
return res.Certs, res.GetLastError()
}
func newSigningBlock(r io.ReadSeeker) (sblock *signingBlock, magic uint32, err error) {
size, err := r.Seek(0, io.SeekEnd)
if err != nil {
err = fmt.Errorf("failed to seek to the end of the APK file: %s", err.Error())
}
if _, err := r.Seek(0, io.SeekStart); err != nil {
err = fmt.Errorf("failed to seek to the start of the APK file: %s", err.Error())
}
if size < 4 {
err = fmt.Errorf("APK file is too short (%d bytes).", size)
return
}
if err = binary.Read(r, binary.LittleEndian, &magic); err != nil {
err = fmt.Errorf("Failed to read APK magic: %s", err.Error())
return
}
sblock = &signingBlock{
file: r,
fileSize: size,
}
return
}
func (s *signingBlock) findEocd() error {
if s.fileSize < eocdRecMinSize {
return fmt.Errorf("APK file is too short (%d bytes).", s.fileSize)
}
if err := s.findEocdMaxCommentSize(0); err == nil {
return nil
}
return s.findEocdMaxCommentSize(math.MaxUint16)
}
func (s *signingBlock) findEocdMaxCommentSize(maxCommentSize int) error {
if maxCommentSize > int(s.fileSize-eocdRecMinSize) {
maxCommentSize = int(s.fileSize - eocdRecMinSize)
}
buf := make([]byte, eocdRecMinSize+maxCommentSize)
bufOffsetInFile := s.fileSize - int64(len(buf))
if _, err := s.file.Seek(bufOffsetInFile, io.SeekStart); err != nil {
return err
}
if _, err := io.ReadFull(s.file, buf); err != nil {
return err
}
maxCommentSize = len(buf) - eocdRecMinSize
if maxCommentSize > math.MaxUint16 {
maxCommentSize = math.MaxUint16
}
emptyCommentStart := len(buf) - eocdRecMinSize
for commentSize := 0; commentSize <= maxCommentSize; commentSize++ {
pos := emptyCommentStart - commentSize
if binary.LittleEndian.Uint32(buf[pos:pos+4]) == eocdRecMagic {
recordCommentSize := binary.LittleEndian.Uint16(buf[pos+eocdCommentSizeOffset:])
if int(recordCommentSize) == commentSize {
s.eocdOffset = bufOffsetInFile + int64(pos)
s.centralDirOffset = int64(binary.LittleEndian.Uint32(buf[pos+eocdCentralDirOffsetOffset:]))
s.eocd = buf[pos:]
if s.centralDirOffset >= s.eocdOffset {
return fmt.Errorf("ZIP Central Directory offset ouf of range: %d. Zip End of Central Directory offset: %d",
s.centralDirOffset, s.eocdOffset)
}
s.centralDirSize = int64(binary.LittleEndian.Uint32(buf[pos+eocdCentralDirSizeOffset:]))
if s.centralDirOffset+int64(s.centralDirSize) != s.eocdOffset {
return errors.New("ZIP Central Directory is not immediately followed by End of Central Directory")
}
return nil
}
}
}
return errEocdNotFound
}
func (s *signingBlock) isZip64() bool {
locatorPos := s.eocdOffset - zip64LocatorSize
if locatorPos < 0 {
return false
}
if _, err := s.file.Seek(locatorPos, io.SeekStart); err != nil {
return false
}
var magic uint32
if err := binary.Read(s.file, binary.LittleEndian, &magic); err != nil {
return false
}
return magic == zip64LocatorMagic
}
func (s *signingBlock) pickScheme(blocks map[BlockId][]byte, minSdkVersion, maxSdkVersion int32) (schemeId int, scheme signatureBlockScheme, block []byte, err error) {
if block = blocks[blockIdSchemeV31]; block != nil {
schemeId = schemeIdV31
scheme = &schemeV31{
backendV31: schemeV3{
actingAsV31: true,
minSdkVersion: minSdkVersion,
maxSdkVersion: maxSdkVersion,
},
backendV3: schemeV3{
minSdkVersion: minSdkVersion,
maxSdkVersion: maxSdkVersion,
},
}
} else if block = blocks[blockIdSchemeV3]; block != nil {
schemeId = schemeIdV3
scheme = &schemeV3{minSdkVersion: minSdkVersion, maxSdkVersion: maxSdkVersion}
} else if block = blocks[blockIdSchemeV2]; block != nil {
schemeId = schemeIdV2
scheme = &schemeV2{minSdkVersion, maxSdkVersion}
} else {
schemeId = schemeIdV1
err = &signingBlockNotFoundError{errors.New("No APK Signature block in APK Signing Block")}
}
return
}
func (s *signingBlock) findApkSigningBlock() (block []byte, offset int64, err error) {
if s.centralDirOffset < apkSigBlockMinSize {
err = errNoSigningBlockSignature
return
}
footer := make([]byte, 24)
if _, err = s.file.Seek(s.centralDirOffset-int64(len(footer)), io.SeekStart); err != nil {
return
}
if _, err = io.ReadFull(s.file, footer); err != nil {
return
}
if binary.LittleEndian.Uint64(footer[8:]) != apkSigBlockMagicLo ||
binary.LittleEndian.Uint64(footer[16:]) != apkSigBlockMagicHi {
err = errNoSigningBlockSignature
return
}
blockSizeFooter := binary.LittleEndian.Uint64(footer)
if blockSizeFooter < uint64(len(footer)) || blockSizeFooter > math.MaxInt32-8 {
err = fmt.Errorf("APK Signing Block size out of range: %d", blockSizeFooter)
return
}
totalSize := int64(blockSizeFooter + 8)
if totalSize < apkSigBlockMinSize {
err = fmt.Errorf("Apk Signing Block is too small: %d vs %d", totalSize, apkSigBlockMinSize)
return
}
offset = int64(s.centralDirOffset) - totalSize
if offset < 0 {
err = fmt.Errorf("APK Signing Block offset out of range: %d", offset)
return
}
block = make([]byte, totalSize)
if _, err = s.file.Seek(offset, io.SeekStart); err != nil {
return
}
if _, err = io.ReadFull(s.file, block); err != nil {
return
}
if blockSizeHeader := binary.LittleEndian.Uint64(block); blockSizeHeader != blockSizeFooter {
err = fmt.Errorf("APK Signing Block sizes in header and footer do not match: %d vs %d",
blockSizeHeader, blockSizeFooter)
return
}
return
}
func (s *signingBlock) findSignatureBlocks(maxSdkVersion int32, res *VerificationResult) (blocks map[BlockId][]byte, frostingRes *frostingInfo, err error) {
if err = s.findEocd(); err != nil {
return
}
if s.isZip64() {
err = errors.New("ZIP64 APK not supported")
return
}
var sigBlock []byte
sigBlock, s.sigBlockOffset, err = s.findApkSigningBlock()
if err != nil {
return
}
pairs := bytes.NewReader(sigBlock[8 : len(sigBlock)-24])
entryCount := 0
blocks = make(map[BlockId][]byte)
for pairs.Len() > 0 {
entryCount++
if pairs.Len() < 8 {
err = fmt.Errorf("Insufficient data to read size of APK Signing Block entry #%d", entryCount)
return
}
var entryLen int64
binary.Read(pairs, binary.LittleEndian, &entryLen)
if entryLen < 4 || entryLen > math.MaxInt32 {
err = fmt.Errorf("APK Signing Block entry #%d size out of range: %d", entryCount, entryLen)
return
}
nextEntryPos := pairs.Size() - int64(pairs.Len()) + entryLen
if entryLen > int64(pairs.Len()) {
err = fmt.Errorf("APK Signing Block entry #%d size out of range: %d, available: %d",
entryCount, entryLen, pairs.Len())
return
}
var id uint32
if err = binary.Read(pairs, binary.LittleEndian, &id); err != nil {
err = fmt.Errorf("failed to read signing block id: %s", err.Error())
return
}
switch bid := BlockId(id); bid {
case blockIdSchemeV2, blockIdSchemeV3, blockIdSchemeV31:
block := make([]byte, entryLen-4)
if _, err = pairs.Read(block); err != nil {
return
}
isSupported := (bid == blockIdSchemeV2 && apilevel.SupportsSigV2(maxSdkVersion)) ||
(bid == blockIdSchemeV3 && apilevel.SupportsSigV3(maxSdkVersion)) ||
(bid == blockIdSchemeV31 && apilevel.SupportsSigV31(maxSdkVersion))
if isSupported {
blocks[bid] = block
}
if !isSupported || (bid == blockIdSchemeV3 && apilevel.SupportsSigV31(maxSdkVersion)) {
if res.ExtraBlocks == nil {
res.ExtraBlocks = make(map[BlockId][]byte)
}
res.ExtraBlocks[bid] = block
}
case blockIdVerityPadding:
// Block full of zeros to ensure the signing block is padded to 4K,
// for verity signature verification.
case blockIdFrosting:
frostingBlock := make([]byte, entryLen-4)
if _, err = pairs.Read(frostingBlock); err != nil {
return
}
var frosting frostingInfo
var frResult FrostingResult
res.Frosting = &frResult
frResult.KeySha256, frResult.ProtobufInfo, frResult.Error = frosting.parse(frostingBlock)
if frResult.Error != nil {
res.addWarning("frosting: %s", frResult.Error.Error())
} else {
frostingRes = &frosting
}
case blockIdSourceStampV2:
block := make([]byte, entryLen-4)
if _, err = pairs.Read(block); err != nil {
return
}
blocks[bid] = block
default:
block := make([]byte, entryLen-4)
if _, err = pairs.Read(block); err != nil {
return
}
if res.ExtraBlocks == nil {
res.ExtraBlocks = make(map[BlockId][]byte)
}
res.ExtraBlocks[bid] = block
}
if _, err = pairs.Seek(nextEntryPos, io.SeekStart); err != nil {
return
}
}
return
}
func (s *signingBlock) extractCerts(scheme signatureBlockScheme, block []byte, res *VerificationResult) map[contentDigest][]byte {
contentDigests := make(map[contentDigest][]byte)
signatureBlock := bytes.NewBuffer(block)
scheme.parseSigners(signatureBlock, contentDigests, res)
return contentDigests
}
func (s *signingBlock) verify(scheme signatureBlockScheme, block []byte, minSdkVersion, maxSdkVersion int32, res *VerificationResult) map[contentDigest][]byte {
contentDigests := s.extractCerts(scheme, block, res)
if len(res.Certs) == 0 {
res.addError("no signers found")
return nil
}
if len(contentDigests) == 0 {
res.addError("no content digests found")
return nil
}
if !s.verifyIntegrity(contentDigests, res) {
return contentDigests
}
scheme.finalizeResult(minSdkVersion, maxSdkVersion, res)
return contentDigests
}
func (s *signingBlock) verifyIntegrity(expectedDigests map[contentDigest][]byte, result *VerificationResult) bool {
beforeApkSigningBlock := &dataSourceApk{file: s.file, start: 0, end: s.sigBlockOffset}
centralDir := &dataSourceApk{file: s.file, start: s.centralDirOffset, end: s.eocdOffset}
eocd := &dataSourceBytes{data: append([]byte(nil), s.eocd...)}
// For the purposes of integrity verification, ZIP End of Central Directory's field Start of
// Central Directory must be considered to point to the offset of the APK Signing Block.
binary.LittleEndian.PutUint32(eocd.data[eocdCentralDirOffsetOffset:], uint32(s.sigBlockOffset))
digestAlgorithms := make([]contentDigest, 0, len(expectedDigests))
for algo := range expectedDigests {
digestAlgorithms = append(digestAlgorithms, algo)
}
actualDigests, err := s.computeContentDigests(digestAlgorithms, beforeApkSigningBlock, centralDir, eocd)
if err != nil {
result.addError("Failed to compute digest(s) of contents: %s", err.Error())
return false
}
for _, algo := range digestAlgorithms {
if !bytes.Equal(expectedDigests[algo], actualDigests[algo]) {
result.addError("%d digest of contents did not verify.", algo)
continue
}
}
return true
}
func (s *signingBlock) computeContentDigests(digestAlgorithms []contentDigest, contents ...dataSource) (map[contentDigest][]byte, error) {
var oneMbAlgos []contentDigest
hasVerity := false
for _, algo := range digestAlgorithms {
switch algo {
case digestChunkedSha256, digestChunkedSha512:
oneMbAlgos = append(oneMbAlgos, algo)
case digestVeritySha256:
hasVerity = true
default:
panic(fmt.Sprintf("unhandled crypto algo %d", int(algo)))
}
}
var result map[contentDigest][]byte
var err error
if len(oneMbAlgos) != 0 {
result, err = s.computeOneMbChunkContentDigests(oneMbAlgos, contents)
if err != nil {
return result, err
}
}
if hasVerity {
if result == nil {
result = make(map[contentDigest][]byte)
}
result[digestVeritySha256], err = s.computeVerityContentDigest(contents)
if err != nil {
return result, err
}
}
if len(result) == 0 {
return nil, fmt.Errorf("no valid digest algorithms")
}
return result, nil
}
func (s *signingBlock) computeOneMbChunkContentDigests(digestAlgorithms []contentDigest, contents []dataSource) (map[contentDigest][]byte, error) {
var totalChunkCount int64
for _, input := range contents {
totalChunkCount += input.chunkCount()
}
if totalChunkCount >= math.MaxInt32/1024 {
return nil, fmt.Errorf("Too many chunks: %d", totalChunkCount)
}
digestsOfChunks := make([][]byte, len(digestAlgorithms))
hashers := make([]hash.Hash, len(digestAlgorithms))
for i, algo := range digestAlgorithms {
buf := make([]byte, 5+totalChunkCount*int64(algo.Hash().Size()))
buf[0] = 0x5a
binary.LittleEndian.PutUint32(buf[1:], uint32(totalChunkCount))
digestsOfChunks[i] = buf
hashers[i] = algo.Hash().New()
}
chunkContentPrefix := make([]byte, 5)
chunkContentPrefix[0] = 0xa5
chunkIndex := 0
for inputIdx, input := range contents {
var offset int64
remaining := input.length()
for remaining > 0 {
chunkSize := remaining
if chunkSize > maxChunkSize {
chunkSize = maxChunkSize
}
binary.LittleEndian.PutUint32(chunkContentPrefix[1:], uint32(chunkSize))
for i := range hashers {
hashers[i].Write(chunkContentPrefix)
if err := input.writeTo(hashers[i], offset, chunkSize); err != nil {
return nil, fmt.Errorf("Failed to digest chunk #%d of section #%d", chunkIndex, inputIdx)
}
sum := hashers[i].Sum(nil)
hashers[i].Reset()
copy(digestsOfChunks[i][5+chunkIndex*len(sum):], sum)
}
offset += chunkSize
remaining -= chunkSize
chunkIndex++
}
}
result := make(map[contentDigest][]byte, len(digestAlgorithms))
for i := range digestsOfChunks {
hashers[i].Write(digestsOfChunks[i])
result[digestAlgorithms[i]] = hashers[i].Sum(nil)
}
return result, nil
}
func (s *signingBlock) computeVerityContentDigest(contents []dataSource) ([]byte, error) {
if contents[0].length()%verityChunkSize != 0 {
return nil, fmt.Errorf("APK Signing block size not a multiple of %d", verityChunkSize)
}
chained := newChainedDataSource(contents...)
builder := newVerityTreeBuilder(make([]byte, 8))
rootHash, err := builder.generateTreeRootHash(chained)
if err != nil {
return nil, fmt.Errorf("failed to generate verity tree root hash: %s", err.Error())
}
result := make([]byte, sha256.Size+8)
copy(result, rootHash)
binary.LittleEndian.PutUint64(result[sha256.Size:], uint64(chained.length()))
return result, nil
}
Go
1
https://gitee.com/naizhe579/apkverifier.git
git@gitee.com:naizhe579/apkverifier.git
naizhe579
apkverifier
apkverifier
v1.0.1

搜索帮助