@Override public void feed(long offset, long size, DataSink sink) throws IOException { checkChunkValid(offset, size); // checkChunkValid combined with the way instances of this class are constructed ensures // that mSliceOffset + offset does not overflow and that it's fine to cast size to int. sink.consume(mArray, (int) (mSliceOffset + offset), (int) size); }
@Override public void inputApkSigningBlock(DataSource apkSigningBlock) { checkNotClosed(); if ((apkSigningBlock == null) || (apkSigningBlock.size() == 0)) { return; } if (mOtherSignersSignaturesPreserved) { // TODO: Preserve blocks other than APK Signature Scheme v2 blocks of signers configured // in this engine. return; } // TODO: Preserve blocks other than APK Signature Scheme v2 blocks. }
@Override public DataSink getDataSink() { synchronized (mLock) { checkNotDone(); if (mDataSinkBuf == null) { mDataSinkBuf = new ByteArrayOutputStream(); } if (mDataSink == null) { mDataSink = DataSinks.asDataSink(mDataSinkBuf); } return mDataSink; } }
/** * Outputs this record, replacing its extra field with the provided one, and returns returns the * number of bytes output. */ public long outputRecordWithModifiedExtra( DataSource sourceApk, ByteBuffer extra, DataSink output) throws IOException { long recordStartOffsetInSource = getStartOffsetInArchive(); int extraStartOffsetInRecord = getExtraFieldStartOffsetInsideRecord(); int extraSizeBytes = extra.remaining(); int headerSize = extraStartOffsetInRecord + extraSizeBytes; ByteBuffer header = ByteBuffer.allocate(headerSize); header.order(ByteOrder.LITTLE_ENDIAN); sourceApk.copyTo(recordStartOffsetInSource, extraStartOffsetInRecord, header); header.put(extra.slice()); header.flip(); ZipUtils.setUnsignedInt16(header, EXTRA_LENGTH_OFFSET, extraSizeBytes); long outputByteCount = header.remaining(); output.consume(header); long remainingRecordSize = getSize() - mDataStartOffset; sourceApk.feed(recordStartOffsetInSource + mDataStartOffset, remainingRecordSize, output); outputByteCount += remainingRecordSize; return outputByteCount; }
long centralDirOffsetForDigesting = beforeCentralDir.size(); ByteBuffer eocdBuf = ByteBuffer.allocate((int) eocd.size()); eocdBuf.order(ByteOrder.LITTLE_ENDIAN); eocd.copyTo(0, (int) eocd.size(), eocdBuf); eocdBuf.flip(); ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, centralDirOffsetForDigesting); beforeCentralDir, centralDir, DataSources.asDataSource(eocdBuf)}); } catch (IOException e) { throw new IOException("Failed to read APK being signed", e);
RandomAccessFile inputFile = new RandomAccessFile(mInputApkFile, "r"); in = inputFile; inputApk = DataSources.asDataSource(inputFile); } else { throw new IllegalStateException("Input APK not specified"); out = outputFile; outputFile.setLength(0); outputApkOut = DataSinks.asDataSink(outputFile); outputApkIn = DataSources.asDataSource(outputFile); } else { throw new IllegalStateException("Output APK not specified");
ByteBuffer footer = apk.getByteBuffer(centralDirStartOffset - 24, 24); footer.order(ByteOrder.LITTLE_ENDIAN); if ((footer.getLong(8) != APK_SIG_BLOCK_MAGIC_LO) "APK Signing Block offset out of range: " + apkSigBlockOffset); ByteBuffer apkSigBlock = apk.getByteBuffer(apkSigBlockOffset, 8); apkSigBlock.order(ByteOrder.LITTLE_ENDIAN); long apkSigBlockSizeInHeader = apkSigBlock.getLong(0); + apkSigBlockSizeInHeader + " vs " + apkSigBlockSizeInFooter); return Pair.of(apk.slice(apkSigBlockOffset, totalSize), apkSigBlockOffset);
/** * Returns the APK Signature Scheme v2 block contained in the provided APK file and the * additional information relevant for verifying the block against the file. * * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2 * @throws IOException if an I/O error occurs while reading the APK */ private static SignatureInfo findSignature( DataSource apk, ApkUtils.ZipSections zipSections, Result result) throws IOException, SignatureNotFoundException { // Find the APK Signing Block. The block immediately precedes the Central Directory. ByteBuffer eocd = zipSections.getZipEndOfCentralDirectory(); Pair<DataSource, Long> apkSigningBlockAndOffset = findApkSigningBlock(apk, zipSections); DataSource apkSigningBlock = apkSigningBlockAndOffset.getFirst(); long apkSigningBlockOffset = apkSigningBlockAndOffset.getSecond(); ByteBuffer apkSigningBlockBuf = apkSigningBlock.getByteBuffer(0, (int) apkSigningBlock.size()); apkSigningBlockBuf.order(ByteOrder.LITTLE_ENDIAN); // Find the APK Signature Scheme v2 Block inside the APK Signing Block. ByteBuffer apkSignatureSchemeV2Block = findApkSignatureSchemeV2Block(apkSigningBlockBuf, result); return new SignatureInfo( apkSignatureSchemeV2Block, apkSigningBlockOffset, zipSections.getZipCentralDirectoryOffset(), zipSections.getZipEndOfCentralDirectoryOffset(), eocd); }
/** * Outputs this record and returns returns the number of bytes output. */ public long outputRecord(DataSource sourceApk, DataSink output) throws IOException { long size = getSize(); sourceApk.feed(getStartOffsetInArchive(), size, output); return size; }
private static ByteBuffer getZipCentralDirectory( DataSource apk, ApkUtils.ZipSections apkSections) throws IOException, ApkFormatException { long cdSizeBytes = apkSections.getZipCentralDirectorySizeBytes(); if (cdSizeBytes > Integer.MAX_VALUE) { throw new ApkFormatException("ZIP Central Directory too large: " + cdSizeBytes); } long cdOffset = apkSections.getZipCentralDirectoryOffset(); ByteBuffer cd = apk.getByteBuffer(cdOffset, (int) cdSizeBytes); cd.order(ByteOrder.LITTLE_ENDIAN); return cd; }
/** * Verifies the provided APK's APK Signature Scheme v2 signatures and returns the result of * verification. APK is considered verified only if {@link Result#verified} is {@code true}. If * verification fails, the result will contain errors -- see {@link Result#getErrors()}. * * @throws ApkFormatException if the APK is malformed * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a * required cryptographic algorithm implementation is missing * @throws SignatureNotFoundException if no APK Signature Scheme v2 signatures are found * @throws IOException if an I/O error occurs when reading the APK */ public static Result verify(DataSource apk, ApkUtils.ZipSections zipSections) throws IOException, ApkFormatException, NoSuchAlgorithmException, SignatureNotFoundException { Result result = new Result(); SignatureInfo signatureInfo = findSignature(apk, zipSections, result); DataSource beforeApkSigningBlock = apk.slice(0, signatureInfo.apkSigningBlockOffset); DataSource centralDir = apk.slice( signatureInfo.centralDirOffset, signatureInfo.eocdOffset - signatureInfo.centralDirOffset); ByteBuffer eocd = signatureInfo.eocd; verify(beforeApkSigningBlock, signatureInfo.signatureBlock, centralDir, eocd, result); return result; }
RandomAccessFile f = new RandomAccessFile(mApkFile, "r"); in = f; apk = DataSources.asDataSource(f, 0, f.length()); } else { throw new IllegalStateException("APK not provided");
@Override public void feed(long offset, long size, DataSink sink) throws IOException { checkChunkValid(offset, size); // checkChunkValid ensures that it's OK to cast offset and size to int. sink.consume(mArray, (int) offset, (int) size); }
if (mDataCompressed) { try (InflateSinkAdapter inflateAdapter = new InflateSinkAdapter(sink)) { lfhSection.feed(dataStartOffsetInArchive, mDataSize, inflateAdapter); long actualUncompressedSize = inflateAdapter.getOutputByteCount(); if (actualUncompressedSize != mUncompressedDataSize) { lfhSection.feed(dataStartOffsetInArchive, mDataSize, sink);
androidManifest = LocalFileRecord.getUncompressedData( lhfSection, androidManifestCdRecord, lhfSection.size()); } catch (ZipFormatException e) { throw new MinSdkVersionException(
ByteBuffer cd = apk.getByteBuffer(cdOffset, (int) cdSizeBytes); cd.order(ByteOrder.LITTLE_ENDIAN);
@Override public void feed(long offset, long size, DataSink sink) throws IOException { if ((size < 0) || (size > mSize)) { throw new IllegalArgumentException("size: " + size + ", source size: " + mSize); } sink.consume(getByteBuffer(offset, (int) size)); }
@Override public void consume(byte[] buf, int offset, int length) throws IOException { checkNotClosed(); mInflater.setInput(buf, offset, length); if (mOutputBuffer == null) { mOutputBuffer = new byte[65536]; } while (!mInflater.finished()) { int outputChunkSize; try { outputChunkSize = mInflater.inflate(mOutputBuffer); } catch (DataFormatException e) { throw new IOException("Failed to inflate data", e); } if (outputChunkSize == 0) { return; } mDelegate.consume(mOutputBuffer, 0, outputChunkSize); mOutputByteCount += outputChunkSize; } }
@Override public void feed(long offset, long size, DataSink sink) throws IOException { long sourceSize = size(); checkChunkValid(offset, size, sourceSize); if (size == 0) { return; } long chunkOffsetInFile = mOffset + offset; long remaining = size; byte[] buf = new byte[(int) Math.min(remaining, MAX_READ_CHUNK_SIZE)]; while (remaining > 0) { int chunkSize = (int) Math.min(remaining, buf.length); synchronized (mFile) { mFile.seek(chunkOffsetInFile); mFile.readFully(buf, 0, chunkSize); } sink.consume(buf, 0, chunkSize); chunkOffsetInFile += chunkSize; remaining -= chunkSize; } }