use of com.android.apksig.util.DataSource in project apksig by venshine.
the class FileChannelDataSourceTest method testFeedsCorrectData_whenFilePartiallyReadWithOffset.
@Test
public void testFeedsCorrectData_whenFilePartiallyReadWithOffset() throws Exception {
byte[] fullFileContent = createFileContent(1024 * 1024 + 987654);
RandomAccessFile raf = createRaf(fullFileContent);
DataSource rafDataSource = new FileChannelDataSource(raf.getChannel());
ByteArrayDataSink dataSink = new ByteArrayDataSink();
int offset = 23456;
int bytesToFeed = 1024 * 1024 + 12345;
rafDataSource.feed(offset, bytesToFeed, dataSink);
byte[] expectedBytes = Arrays.copyOfRange(fullFileContent, offset, offset + bytesToFeed);
byte[] resultBytes = getDataSinkBytes(dataSink);
assertArrayEquals(expectedBytes, resultBytes);
}
use of com.android.apksig.util.DataSource in project apksig by venshine.
the class ChainedDataSource method feed.
@Override
public void feed(long offset, long size, DataSink sink) throws IOException {
if (offset + size > mTotalSize) {
throw new IndexOutOfBoundsException("Requested more than available");
}
for (DataSource src : mSources) {
// Offset is beyond the current source. Skip.
if (offset >= src.size()) {
offset -= src.size();
continue;
}
// If the remaining is enough, finish it.
long remaining = src.size() - offset;
if (remaining >= size) {
src.feed(offset, size, sink);
break;
}
// If the remaining is not enough, consume all.
src.feed(offset, remaining, sink);
size -= remaining;
offset = 0;
}
}
use of com.android.apksig.util.DataSource in project apksig by venshine.
the class ChainedDataSource method slice.
@Override
public DataSource slice(long offset, long size) {
// Find the first slice.
Pair<Integer, Long> firstSource = locateDataSource(offset);
int beginIndex = firstSource.getFirst();
long beginLocalOffset = firstSource.getSecond();
DataSource beginSource = mSources[beginIndex];
if (beginLocalOffset + size <= beginSource.size()) {
return beginSource.slice(beginLocalOffset, size);
}
// Add the first slice to chaining, followed by the middle full slices, then the last.
ArrayList<DataSource> sources = new ArrayList<>();
sources.add(beginSource.slice(beginLocalOffset, beginSource.size() - beginLocalOffset));
Pair<Integer, Long> lastSource = locateDataSource(offset + size - 1);
int endIndex = lastSource.getFirst();
long endLocalOffset = lastSource.getSecond();
for (int i = beginIndex + 1; i < endIndex; i++) {
sources.add(mSources[i]);
}
sources.add(mSources[endIndex].slice(0, endLocalOffset + 1));
return new ChainedDataSource(sources.toArray(new DataSource[0]));
}
use of com.android.apksig.util.DataSource in project apksig by venshine.
the class VerityTreeBuilder method generateVerityTree.
/**
* Returns the byte buffer that contains the whole verity tree.
*
* The tree is built bottom up. The bottom level has 256-bit digest for each 4 KB block in the
* input file. If the total size is larger than 4 KB, take this level as input and repeat the
* same procedure, until the level is within 4 KB. If salt is given, it will apply to each
* digestion before the actual data.
*
* The returned root hash is calculated from the last level of 4 KB chunk, similarly with salt.
*
* The tree is currently stored only in memory and is never written out. Nevertheless, it is
* the actual verity tree format on disk, and is supposed to be re-generated on device.
*/
public ByteBuffer generateVerityTree(DataSource fileSource) throws IOException {
int digestSize = mMd.getDigestLength();
// Calculate the summed area table of level size. In other word, this is the offset
// table of each level, plus the next non-existing level.
int[] levelOffset = calculateLevelOffset(fileSource.size(), digestSize);
ByteBuffer verityBuffer = ByteBuffer.allocate(levelOffset[levelOffset.length - 1]);
// Generate the hash tree bottom-up.
for (int i = levelOffset.length - 2; i >= 0; i--) {
DataSink middleBufferSink = new ByteBufferSink(slice(verityBuffer, levelOffset[i], levelOffset[i + 1]));
DataSource src;
if (i == levelOffset.length - 2) {
src = fileSource;
digestDataByChunks(src, middleBufferSink);
} else {
src = DataSources.asDataSource(slice(verityBuffer.asReadOnlyBuffer(), levelOffset[i + 1], levelOffset[i + 2]));
digestDataByChunks(src, middleBufferSink);
}
// If the output is not full chunk, pad with 0s.
long totalOutput = divideRoundup(src.size(), CHUNK_SIZE) * digestSize;
int incomplete = (int) (totalOutput % CHUNK_SIZE);
if (incomplete > 0) {
byte[] padding = new byte[CHUNK_SIZE - incomplete];
middleBufferSink.consume(padding, 0, padding.length);
}
}
return verityBuffer;
}
use of com.android.apksig.util.DataSource in project LSPatch by LSPosed.
the class SigningExtension method onOutputZipEntriesWritten.
private void onOutputZipEntriesWritten() throws IOException {
if (!dirty) {
return;
}
// Check whether we should output an APK Signing Block which contains v2 signatures
byte[] apkSigningBlock;
byte[] centralDirBytes = zFile.getCentralDirectoryBytes();
byte[] eocdBytes = zFile.getEocdBytes();
ApkSignerEngine.OutputApkSigningBlockRequest2 addV2SignatureRequest;
// file (as reported to this extension).
if (cachedApkSigningBlock != null) {
apkSigningBlock = cachedApkSigningBlock;
addV2SignatureRequest = null;
} else {
DataSource centralDir = DataSources.asDataSource(ByteBuffer.wrap(centralDirBytes));
DataSource eocd = DataSources.asDataSource(ByteBuffer.wrap(eocdBytes));
long zipEntriesSizeBytes = zFile.getCentralDirectoryOffset() - zFile.getExtraDirectoryOffset();
DataSource zipEntries = zFile.asDataSource(0, zipEntriesSizeBytes);
try {
addV2SignatureRequest = signer.outputZipSections2(zipEntries, centralDir, eocd);
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | ApkFormatException | IOException e) {
throw new IOException("Failed to generate v2 signature", e);
}
if (addV2SignatureRequest != null) {
apkSigningBlock = addV2SignatureRequest.getApkSigningBlock();
if (sdkDependencyData != null) {
apkSigningBlock = SigningBlockUtils.addToSigningBlock(apkSigningBlock, sdkDependencyData, DEPENDENCY_INFO_BLOCK_ID);
}
apkSigningBlock = Bytes.concat(new byte[addV2SignatureRequest.getPaddingSizeBeforeApkSigningBlock()], apkSigningBlock);
} else {
apkSigningBlock = new byte[0];
if (sdkDependencyData != null) {
apkSigningBlock = SigningBlockUtils.addToSigningBlock(apkSigningBlock, sdkDependencyData, DEPENDENCY_INFO_BLOCK_ID);
int paddingSize = ApkSigningBlockUtils.generateApkSigningBlockPadding(zipEntries, /* apkSigningBlockPaddingSupported */
true).getSecond();
apkSigningBlock = Bytes.concat(new byte[paddingSize], apkSigningBlock);
}
}
cachedApkSigningBlock = apkSigningBlock;
}
// Insert the APK Signing Block into the output right before the ZIP Central Directory and
// accordingly update the start offset of ZIP Central Directory in ZIP End of Central
// Directory.
zFile.directWrite(zFile.getCentralDirectoryOffset() - zFile.getExtraDirectoryOffset(), apkSigningBlock);
zFile.setExtraDirectoryOffset(apkSigningBlock.length);
if (addV2SignatureRequest != null) {
addV2SignatureRequest.done();
}
}
Aggregations