mirror of
https://github.com/revanced/ARSCLib.git
synced 2025-05-01 06:34:26 +02:00
implement basic structure of APK signature block
This commit is contained in:
parent
2b6da361dc
commit
32a82f9527
@ -15,13 +15,10 @@
|
||||
*/
|
||||
package com.reandroid.archive2;
|
||||
|
||||
import com.reandroid.archive2.block.CentralEntryHeader;
|
||||
import com.reandroid.archive2.block.EndRecord;
|
||||
import com.reandroid.archive2.block.LocalFileHeader;
|
||||
import com.reandroid.archive2.block.*;
|
||||
import com.reandroid.archive2.io.ArchiveFile;
|
||||
import com.reandroid.archive2.io.ArchiveUtil;
|
||||
import com.reandroid.archive2.io.ZipSource;
|
||||
import com.reandroid.archive2.model.ApkSigBlock;
|
||||
import com.reandroid.archive2.model.LocalFileDirectory;
|
||||
|
||||
import java.io.File;
|
||||
@ -38,7 +35,7 @@ public class Archive {
|
||||
private final ZipSource zipSource;
|
||||
private final List<ArchiveEntry> entryList;
|
||||
private final EndRecord endRecord;
|
||||
private final ApkSigBlock apkSigBlock;
|
||||
private final ApkSignatureBlock apkSignatureBlock;
|
||||
public Archive(ZipSource zipSource) throws IOException {
|
||||
this.zipSource = zipSource;
|
||||
LocalFileDirectory lfd = new LocalFileDirectory();
|
||||
@ -54,7 +51,7 @@ public class Archive {
|
||||
}
|
||||
this.entryList = entryList;
|
||||
this.endRecord = lfd.getCentralFileDirectory().getEndRecord();
|
||||
this.apkSigBlock = lfd.getApkSigBlock();
|
||||
this.apkSignatureBlock = lfd.getApkSigBlock();
|
||||
}
|
||||
public Archive(File file) throws IOException {
|
||||
this(new ArchiveFile(file));
|
||||
@ -74,8 +71,8 @@ public class Archive {
|
||||
return entryList;
|
||||
}
|
||||
|
||||
public ApkSigBlock getApkSigBlock() {
|
||||
return apkSigBlock;
|
||||
public ApkSignatureBlock getApkSigBlock() {
|
||||
return apkSignatureBlock;
|
||||
}
|
||||
public EndRecord getEndRecord() {
|
||||
return endRecord;
|
||||
@ -104,4 +101,12 @@ public class Archive {
|
||||
String name = archiveEntry.getName().replace('/', File.separatorChar);
|
||||
return new File(dir, name);
|
||||
}
|
||||
// for test
|
||||
public void writeSignatureData(File dir) throws IOException{
|
||||
ApkSignatureBlock apkSignatureBlock = getApkSigBlock();
|
||||
if(apkSignatureBlock == null){
|
||||
throw new IOException("Does not have signature block");
|
||||
}
|
||||
apkSignatureBlock.writeSignatureData(dir);
|
||||
}
|
||||
}
|
||||
|
@ -1,202 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block;
|
||||
|
||||
import com.reandroid.arsc.decoder.ValueDecoder;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ApkSignature extends ZipBlock{
|
||||
public ApkSignature() {
|
||||
super(MIN_SIZE);
|
||||
}
|
||||
public boolean isValid(){
|
||||
return getSigSize() > MIN_SIZE && getId() != null;
|
||||
}
|
||||
@Override
|
||||
public int readBytes(InputStream inputStream) throws IOException {
|
||||
setBytesLength(8, false);
|
||||
byte[] bytes = getBytesInternal();
|
||||
int read = inputStream.read(bytes, 0, bytes.length);
|
||||
if(read != bytes.length){
|
||||
return read;
|
||||
}
|
||||
int offset = bytes.length;
|
||||
int length = (int) getSigSize();
|
||||
setSigSize(length);
|
||||
|
||||
bytes = getBytesInternal();
|
||||
int dataRead = inputStream.read(bytes, offset, length);
|
||||
if(dataRead != length){
|
||||
setSigSize(0);
|
||||
}
|
||||
return read + dataRead;
|
||||
}
|
||||
public long getSigSize(){
|
||||
return getLong(OFFSET_size);
|
||||
}
|
||||
public void setSigSize(long size){
|
||||
int length = (int) (OFFSET_id + size);
|
||||
setBytesLength(length, false);
|
||||
putLong(OFFSET_size, size);
|
||||
}
|
||||
public int getIdValue(){
|
||||
return getInteger(OFFSET_id);
|
||||
}
|
||||
public Id getId(){
|
||||
return Id.valueOf(getIdValue());
|
||||
}
|
||||
public void setId(int id){
|
||||
putInteger(OFFSET_id, id);
|
||||
}
|
||||
public void setId(Id signatureId){
|
||||
setId(signatureId == null? 0 : signatureId.getId());
|
||||
}
|
||||
public byte[] getData(){
|
||||
return getBytes(OFFSET_data, (int) getSigSize() - 4, true);
|
||||
}
|
||||
public void setData(byte[] data){
|
||||
if(data == null){
|
||||
data = new byte[0];
|
||||
}
|
||||
setData(data, 0, data.length);
|
||||
}
|
||||
public void setData(byte[] data, int offset, int length){
|
||||
setSigSize(length);
|
||||
putBytes(data, offset, OFFSET_data, data.length);
|
||||
}
|
||||
public void writeData(OutputStream outputStream) throws IOException{
|
||||
byte[] bytes = getBytesInternal();
|
||||
outputStream.write(bytes, OFFSET_data, (int) (getSigSize() - 4));
|
||||
}
|
||||
public void writeData(File file) throws IOException{
|
||||
File dir = file.getParentFile();
|
||||
if(dir != null && !dir.exists()){
|
||||
dir.mkdirs();
|
||||
}
|
||||
FileOutputStream outputStream = new FileOutputStream(file);
|
||||
writeData(outputStream);
|
||||
outputStream.close();
|
||||
}
|
||||
public File writeToDir(File dir) throws IOException{
|
||||
File file = new File(dir, getId().toFileName());
|
||||
writeData(file);
|
||||
return file;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return getId() + ", size=" + getSigSize();
|
||||
}
|
||||
|
||||
|
||||
public static class Id {
|
||||
private final String name;
|
||||
private final int id;
|
||||
|
||||
private Id(String name, int id) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
}
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
public String toFileName(){
|
||||
if(this.name != null){
|
||||
return name + FILE_EXTENSION;
|
||||
}
|
||||
return String.format("0x%08x", id) + FILE_EXTENSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Id that = (Id) obj;
|
||||
return id == that.id;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
String name = this.name;
|
||||
if(name != null){
|
||||
return name;
|
||||
}
|
||||
return "UNKNOWN("+String.format("0x%08x", id)+")";
|
||||
}
|
||||
public static Id valueOf(String name){
|
||||
if(name == null){
|
||||
return null;
|
||||
}
|
||||
String ext = FILE_EXTENSION;
|
||||
if(name.endsWith(ext)){
|
||||
name = name.substring(0, name.length()-ext.length());
|
||||
}
|
||||
for(Id signatureId:VALUES){
|
||||
if(name.equalsIgnoreCase(signatureId.name())){
|
||||
return signatureId;
|
||||
}
|
||||
}
|
||||
if(ValueDecoder.isHex(name)){
|
||||
return new Id(null, ValueDecoder.parseHex(name));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static Id valueOf(int id){
|
||||
if(id == 0){
|
||||
return null;
|
||||
}
|
||||
for(Id signatureId:VALUES){
|
||||
if(id == signatureId.getId()){
|
||||
return signatureId;
|
||||
}
|
||||
}
|
||||
return new Id(null, id);
|
||||
}
|
||||
public static Id[] values() {
|
||||
return VALUES.clone();
|
||||
}
|
||||
public static final Id V2 = new Id("V2", 0x7109871A);
|
||||
public static final Id V3 = new Id("V3",0xF05368C0);
|
||||
public static final Id V31 = new Id("V31",0x1B93AD61);
|
||||
public static final Id STAMP_V1 = new Id("STAMP_V1", 0x2B09189E);
|
||||
public static final Id STAMP_V2 = new Id("STAMP_V2", 0x6DFF800D);
|
||||
public static final Id PADDING = new Id("PADDING", 0x42726577);
|
||||
|
||||
|
||||
private static final Id[] VALUES = new Id[]{
|
||||
V2, V3, V31, STAMP_V1, STAMP_V2, PADDING
|
||||
};
|
||||
|
||||
private static final String FILE_EXTENSION = ".bin";
|
||||
}
|
||||
|
||||
public static final int MIN_SIZE = 12;
|
||||
|
||||
private static final int OFFSET_size = 0;
|
||||
private static final int OFFSET_id = 8;
|
||||
private static final int OFFSET_data = 12;
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class ApkSignatureBlock extends LengthPrefixedList<SignatureInfo> {
|
||||
public ApkSignatureBlock(SignatureFooter signatureFooter){
|
||||
super(true);
|
||||
setBottomBlock(signatureFooter);
|
||||
}
|
||||
public List<SignatureInfo> getSignatures(){
|
||||
return super.getElements();
|
||||
}
|
||||
public int countSignatures(){
|
||||
return super.getElementsCount();
|
||||
}
|
||||
public SignatureInfo getSignature(SignatureId signatureId){
|
||||
for(SignatureInfo signatureInfo:getSignatures()){
|
||||
if(signatureInfo.getId().equals(signatureId)){
|
||||
return signatureInfo;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public SignatureFooter getSignatureFooter(){
|
||||
return (SignatureFooter) getBottomBlock();
|
||||
}
|
||||
@Override
|
||||
public SignatureInfo newInstance() {
|
||||
return new SignatureInfo();
|
||||
}
|
||||
@Override
|
||||
protected void onRefreshed(){
|
||||
SignatureFooter footer = getSignatureFooter();
|
||||
footer.updateMagic();
|
||||
super.onRefreshed();
|
||||
footer.setSignatureSize(getDataSize());
|
||||
}
|
||||
// for test
|
||||
public void writeSignatureData(File dir) throws IOException{
|
||||
for(SignatureInfo signatureInfo:getElements()){
|
||||
signatureInfo.writeToDir(dir);
|
||||
}
|
||||
}
|
||||
|
||||
private static final long CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES = 1024 * 1024;
|
||||
public static final int ANDROID_COMMON_PAGE_ALIGNMENT_BYTES = 4096;
|
||||
|
||||
private static final byte[] APK_SIGNING_BLOCK_MAGIC = new byte[] {
|
||||
0x41, 0x50, 0x4b, 0x20, 0x53, 0x69, 0x67, 0x20,
|
||||
0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x34, 0x32,
|
||||
};
|
||||
|
||||
public static final int VERSION_SOURCE_STAMP = 0;
|
||||
public static final int VERSION_JAR_SIGNATURE_SCHEME = 1;
|
||||
public static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2;
|
||||
public static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3;
|
||||
public static final int VERSION_APK_SIGNATURE_SCHEME_V31 = 31;
|
||||
public static final int VERSION_APK_SIGNATURE_SCHEME_V4 = 4;
|
||||
|
||||
}
|
36
src/main/java/com/reandroid/archive2/block/BottomBlock.java
Normal file
36
src/main/java/com/reandroid/archive2/block/BottomBlock.java
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block;
|
||||
|
||||
import com.reandroid.arsc.container.BlockList;
|
||||
import com.reandroid.arsc.io.BlockReader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
// General purpose block to consume the remaining bytes of BlockReader
|
||||
public class BottomBlock extends BlockList<LengthPrefixedBytes> {
|
||||
public BottomBlock(){
|
||||
super();
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
while (reader.isAvailable()){
|
||||
LengthPrefixedBytes prefixedBytes = new LengthPrefixedBytes(false);
|
||||
prefixedBytes.readBytes(reader);
|
||||
this.add(prefixedBytes);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public class CertificateBlock extends LengthPrefixedBytes{
|
||||
public CertificateBlock() {
|
||||
super(false);
|
||||
}
|
||||
|
||||
public X509Certificate getCertificate(){
|
||||
return generateCertificate(getByteArray().toArray());
|
||||
}
|
||||
public static X509Certificate generateCertificate(byte[] encodedForm){
|
||||
CertificateFactory factory = getCertFactory();
|
||||
if(factory == null){
|
||||
return null;
|
||||
}
|
||||
try{
|
||||
// TODO: cert bytes could be in DER format ?
|
||||
return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(encodedForm));
|
||||
}catch (CertificateException ignored){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
private static CertificateFactory getCertFactory() {
|
||||
if (sCertFactory == null) {
|
||||
try {
|
||||
sCertFactory = CertificateFactory.getInstance("X.509");
|
||||
} catch (CertificateException ignored) {
|
||||
}
|
||||
}
|
||||
return sCertFactory;
|
||||
}
|
||||
|
||||
private static CertificateFactory sCertFactory = null;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block;
|
||||
|
||||
|
||||
public class CertificateBlockList extends LengthPrefixedList<CertificateBlock>{
|
||||
public CertificateBlockList() {
|
||||
super(false);
|
||||
}
|
||||
@Override
|
||||
public CertificateBlock newInstance() {
|
||||
return new CertificateBlock();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block;
|
||||
|
||||
import com.reandroid.arsc.base.Block;
|
||||
import com.reandroid.arsc.container.ExpandableBlockContainer;
|
||||
import com.reandroid.arsc.io.BlockReader;
|
||||
import com.reandroid.arsc.item.IntegerItem;
|
||||
import com.reandroid.arsc.item.LongItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class LengthPrefixedBlock extends ExpandableBlockContainer{
|
||||
private final Block numberBlock;
|
||||
public LengthPrefixedBlock(int childesCount, boolean is_long) {
|
||||
super(1 + childesCount);
|
||||
Block numberBlock;
|
||||
if(is_long){
|
||||
numberBlock = new LongItem();
|
||||
}else {
|
||||
numberBlock = new IntegerItem();
|
||||
}
|
||||
this.numberBlock = numberBlock;
|
||||
addChild(this.numberBlock);
|
||||
}
|
||||
public long getDataSize(){
|
||||
Block numberBlock = this.numberBlock;
|
||||
if(numberBlock instanceof LongItem){
|
||||
return ((LongItem)numberBlock).get();
|
||||
}
|
||||
return ((IntegerItem)numberBlock).get();
|
||||
}
|
||||
public void setDataSize(long dataSize){
|
||||
Block numberBlock = this.numberBlock;
|
||||
if(numberBlock instanceof LongItem){
|
||||
((LongItem)numberBlock).set(dataSize);
|
||||
}else {
|
||||
((IntegerItem)numberBlock).set((int) dataSize);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
if(!reader.isAvailable()){
|
||||
return;
|
||||
}
|
||||
Block numberBlock = this.numberBlock;
|
||||
numberBlock.readBytes(reader);
|
||||
int dataSize = (int) getDataSize();
|
||||
if(dataSize <= 0){
|
||||
onSizeLoaded(0);
|
||||
return;
|
||||
}
|
||||
onSizeLoaded(dataSize);
|
||||
BlockReader chunkReader = reader.create(dataSize);
|
||||
Block[] childes = getChildes();
|
||||
for(int i=0;i<childes.length;i++){
|
||||
Block child = childes[i];
|
||||
if(child == numberBlock){
|
||||
continue;
|
||||
}
|
||||
child.readBytes(chunkReader);
|
||||
}
|
||||
reader.offset(dataSize);
|
||||
}
|
||||
protected void onSizeLoaded(int dataSize){
|
||||
}
|
||||
@Override
|
||||
protected void onRefreshed(){
|
||||
int size = countBytes() - numberBlock.countBytes();
|
||||
setDataSize(size);
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return "size=" + numberBlock;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block;
|
||||
|
||||
import com.reandroid.arsc.item.ByteArray;
|
||||
|
||||
public class LengthPrefixedBytes extends LengthPrefixedBlock{
|
||||
private final ByteArray byteArray;
|
||||
public LengthPrefixedBytes(boolean is_long) {
|
||||
super(1, is_long);
|
||||
this.byteArray = new ByteArray();
|
||||
addChild(this.byteArray);
|
||||
}
|
||||
|
||||
public ByteArray getByteArray() {
|
||||
return byteArray;
|
||||
}
|
||||
@Override
|
||||
protected void onSizeLoaded(int dataSize){
|
||||
this.byteArray.setSize(dataSize);
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block;
|
||||
|
||||
import com.reandroid.arsc.base.Block;
|
||||
import com.reandroid.arsc.base.BlockCreator;
|
||||
import com.reandroid.arsc.container.BlockList;
|
||||
import com.reandroid.arsc.container.FixedBlockContainer;
|
||||
import com.reandroid.arsc.container.SingleBlockContainer;
|
||||
import com.reandroid.arsc.io.BlockReader;
|
||||
import com.reandroid.arsc.item.IntegerItem;
|
||||
import com.reandroid.arsc.item.LongItem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class LengthPrefixedList<T extends Block> extends FixedBlockContainer
|
||||
implements BlockCreator<T> {
|
||||
private final Block numberBlock;
|
||||
private final BlockList<T> elements;
|
||||
private final SingleBlockContainer<Block> bottomContainer;
|
||||
public LengthPrefixedList(boolean is_long){
|
||||
super(3);
|
||||
Block numberBlock;
|
||||
if(is_long){
|
||||
numberBlock = new LongItem();
|
||||
}else {
|
||||
numberBlock = new IntegerItem();
|
||||
}
|
||||
this.numberBlock = numberBlock;
|
||||
this.elements = new BlockList<>();
|
||||
this.bottomContainer = new SingleBlockContainer<>();
|
||||
addChild(0, this.numberBlock);
|
||||
addChild(1, this.elements);
|
||||
addChild(2, this.bottomContainer);
|
||||
}
|
||||
public long getDataSize(){
|
||||
Block numberBlock = this.numberBlock;
|
||||
if(numberBlock instanceof LongItem){
|
||||
return ((LongItem)numberBlock).get();
|
||||
}
|
||||
return ((IntegerItem)numberBlock).get();
|
||||
}
|
||||
public void setDataSize(long dataSize){
|
||||
Block numberBlock = this.numberBlock;
|
||||
if(numberBlock instanceof LongItem){
|
||||
((LongItem)numberBlock).set(dataSize);
|
||||
}else {
|
||||
((IntegerItem)numberBlock).set((int) dataSize);
|
||||
}
|
||||
}
|
||||
public int getElementsCount(){
|
||||
return getElements().size();
|
||||
}
|
||||
public List<T> getElements() {
|
||||
return elements.getChildes();
|
||||
}
|
||||
public T add(T element){
|
||||
this.elements.add(element);
|
||||
return element;
|
||||
}
|
||||
public boolean remove(T element){
|
||||
return this.elements.remove(element);
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException{
|
||||
if(!reader.isAvailable()){
|
||||
return;
|
||||
}
|
||||
numberBlock.readBytes(reader);
|
||||
int totalSize = (int) getDataSize();
|
||||
if(totalSize <= 0){
|
||||
return;
|
||||
}
|
||||
BlockReader chunkReader = reader.create(totalSize);
|
||||
readElements(chunkReader);
|
||||
if(chunkReader.isAvailable()){
|
||||
String junk = "";
|
||||
}
|
||||
bottomContainer.readBytes(chunkReader);
|
||||
reader.offset(totalSize);
|
||||
}
|
||||
private void readElements(BlockReader reader) throws IOException{
|
||||
int preserve = bottomContainer.countBytes() + 4;
|
||||
while (reader.available() > preserve){
|
||||
int position = reader.getPosition();
|
||||
T element = newInstance();
|
||||
element = add(element);
|
||||
element.readBytes(reader);
|
||||
if(position == reader.getPosition()){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void onRefreshed(){
|
||||
int size = countBytes() - numberBlock.countBytes();
|
||||
setDataSize(size);
|
||||
}
|
||||
public Block getBottomBlock(){
|
||||
return bottomContainer.getItem();
|
||||
}
|
||||
public void setBottomBlock(Block block){
|
||||
bottomContainer.setItem(block);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "size=" + numberBlock + ", count=" + getElementsCount();
|
||||
}
|
||||
}
|
@ -31,25 +31,37 @@ public class SignatureFooter extends ZipBlock{
|
||||
byte[] bytes = getBytesInternal();
|
||||
return inputStream.read(bytes, 0, bytes.length);
|
||||
}
|
||||
public long getSigBlockSizeInFooter(){
|
||||
public long getSignatureSize(){
|
||||
return getLong(OFFSET_size);
|
||||
}
|
||||
public void setSigBlockSizeInFooter(long size){
|
||||
public void setSignatureSize(long size){
|
||||
int minLength = MIN_SIZE;
|
||||
if(countBytes() < minLength){
|
||||
setBytesLength(minLength, false);
|
||||
}
|
||||
putLong(OFFSET_size, size);
|
||||
}
|
||||
public byte[] getMagic() {
|
||||
return getBytes(OFFSET_magic, APK_SIG_BLOCK_MAGIC.length, false);
|
||||
}
|
||||
public void setMagic(byte[] magic){
|
||||
if(magic == null){
|
||||
magic = new byte[0];
|
||||
}
|
||||
int length = OFFSET_magic + magic.length;
|
||||
setBytesLength(length, false);
|
||||
putBytes(magic, 0, OFFSET_magic, magic.length);
|
||||
}
|
||||
public boolean isValid(){
|
||||
return getSigBlockSizeInFooter() > MIN_SIZE
|
||||
return getSignatureSize() > MIN_SIZE
|
||||
&& ByteArray.equals(APK_SIG_BLOCK_MAGIC, getMagic());
|
||||
}
|
||||
public void updateMagic(){
|
||||
setMagic(APK_SIG_BLOCK_MAGIC);
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return getSigBlockSizeInFooter() + " ["+new String(getMagic())+"]";
|
||||
return getSignatureSize() + " ["+new String(getMagic())+"]";
|
||||
}
|
||||
|
||||
public static final int MIN_SIZE = 24;
|
||||
|
109
src/main/java/com/reandroid/archive2/block/SignatureId.java
Normal file
109
src/main/java/com/reandroid/archive2/block/SignatureId.java
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block;
|
||||
|
||||
import com.reandroid.arsc.decoder.ValueDecoder;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class SignatureId {
|
||||
private final String name;
|
||||
private final int id;
|
||||
|
||||
private SignatureId(String name, int id) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
}
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
public String toFileName() {
|
||||
if (this.name != null) {
|
||||
return name + FILE_EXTENSION;
|
||||
}
|
||||
return String.format("0x%08x", id) + FILE_EXTENSION;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
SignatureId that = (SignatureId) obj;
|
||||
return id == that.id;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = this.name;
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
return "UNKNOWN(" + String.format("0x%08x", id) + ")";
|
||||
}
|
||||
public static SignatureId valueOf(String name) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
String ext = FILE_EXTENSION;
|
||||
if (name.endsWith(ext)) {
|
||||
name = name.substring(0, name.length() - ext.length());
|
||||
}
|
||||
for (SignatureId signatureId : VALUES) {
|
||||
if (name.equalsIgnoreCase(signatureId.name())) {
|
||||
return signatureId;
|
||||
}
|
||||
}
|
||||
if (ValueDecoder.isHex(name)) {
|
||||
return new SignatureId(null, ValueDecoder.parseHex(name));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static SignatureId valueOf(int id) {
|
||||
for (SignatureId signatureId : VALUES) {
|
||||
if (id == signatureId.getId()) {
|
||||
return signatureId;
|
||||
}
|
||||
}
|
||||
return new SignatureId(null, id);
|
||||
}
|
||||
|
||||
public static SignatureId[] values() {
|
||||
return VALUES.clone();
|
||||
}
|
||||
|
||||
public static final SignatureId V2 = new SignatureId("V2", 0x7109871A);
|
||||
public static final SignatureId V3 = new SignatureId("V3", 0xF05368C0);
|
||||
public static final SignatureId V31 = new SignatureId("V31", 0x1B93AD61);
|
||||
public static final SignatureId STAMP_V1 = new SignatureId("STAMP_V1", 0x2B09189E);
|
||||
public static final SignatureId STAMP_V2 = new SignatureId("STAMP_V2", 0x6DFF800D);
|
||||
public static final SignatureId PADDING = new SignatureId("PADDING", 0x42726577);
|
||||
public static final SignatureId NULL = new SignatureId("NULL", 0x0);
|
||||
|
||||
private static final SignatureId[] VALUES = new SignatureId[]{
|
||||
V2, V3, V31, STAMP_V1, STAMP_V2, PADDING, NULL
|
||||
};
|
||||
|
||||
private static final String FILE_EXTENSION = ".bin";
|
||||
}
|
111
src/main/java/com/reandroid/archive2/block/SignatureInfo.java
Normal file
111
src/main/java/com/reandroid/archive2/block/SignatureInfo.java
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block;
|
||||
|
||||
import com.reandroid.archive2.block.pad.SchemePadding;
|
||||
import com.reandroid.archive2.block.stamp.SchemeStampV1;
|
||||
import com.reandroid.archive2.block.stamp.SchemeStampV2;
|
||||
import com.reandroid.archive2.block.v2.SchemeV2;
|
||||
import com.reandroid.archive2.block.v3.SchemeV3;
|
||||
import com.reandroid.archive2.block.v3.SchemeV31;
|
||||
import com.reandroid.arsc.base.Block;
|
||||
import com.reandroid.arsc.container.SingleBlockContainer;
|
||||
import com.reandroid.arsc.io.BlockLoad;
|
||||
import com.reandroid.arsc.io.BlockReader;
|
||||
import com.reandroid.arsc.item.IntegerItem;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class SignatureInfo extends LengthPrefixedBlock implements BlockLoad {
|
||||
private final IntegerItem idItem;
|
||||
private final SingleBlockContainer<SignatureScheme> schemeContainer;
|
||||
public SignatureInfo() {
|
||||
super(2, true);
|
||||
this.idItem = new IntegerItem();
|
||||
this.schemeContainer = new SingleBlockContainer<>();
|
||||
addChild(this.idItem);
|
||||
addChild(this.schemeContainer);
|
||||
this.idItem.setBlockLoad(this);
|
||||
}
|
||||
public int getIdValue(){
|
||||
return idItem.get();
|
||||
}
|
||||
public SignatureId getId(){
|
||||
return SignatureId.valueOf(getIdValue());
|
||||
}
|
||||
public void setId(int id){
|
||||
idItem.set(id);
|
||||
}
|
||||
public void setId(SignatureId signatureId){
|
||||
setId(signatureId == null? 0 : signatureId.getId());
|
||||
}
|
||||
public SignatureScheme getSignatureScheme(){
|
||||
return schemeContainer.getItem();
|
||||
}
|
||||
public void setSignatureScheme(SignatureScheme signatureScheme){
|
||||
schemeContainer.setItem(signatureScheme);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockLoaded(BlockReader reader, Block sender) throws IOException {
|
||||
if(sender == this.idItem){
|
||||
onIdLoaded();
|
||||
}
|
||||
}
|
||||
private void onIdLoaded(){
|
||||
SignatureId signatureId = getId();
|
||||
SignatureScheme scheme;
|
||||
if(signatureId == SignatureId.V2){
|
||||
scheme = new SchemeV2();
|
||||
}else if(signatureId == SignatureId.V3){
|
||||
scheme = new SchemeV3();
|
||||
}else if(signatureId == SignatureId.V31){
|
||||
scheme = new SchemeV31();
|
||||
}else if(signatureId == SignatureId.STAMP_V1){
|
||||
scheme = new SchemeStampV1();
|
||||
}else if(signatureId == SignatureId.STAMP_V2){
|
||||
scheme = new SchemeStampV2();
|
||||
}else if(signatureId == SignatureId.PADDING){
|
||||
scheme = new SchemePadding();
|
||||
}else {
|
||||
scheme = new UnknownScheme(signatureId);
|
||||
}
|
||||
schemeContainer.setItem(scheme);
|
||||
}
|
||||
// for test
|
||||
public void writeData(File file) throws IOException{
|
||||
File dir = file.getParentFile();
|
||||
if(dir != null && !dir.exists()){
|
||||
dir.mkdirs();
|
||||
}
|
||||
FileOutputStream outputStream = new FileOutputStream(file);
|
||||
writeBytes(outputStream);
|
||||
outputStream.close();
|
||||
}
|
||||
// for test
|
||||
public File writeToDir(File dir) throws IOException{
|
||||
File file = new File(dir, getId().toFileName());
|
||||
writeData(file);
|
||||
return file;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return getId() + ", scheme: " + getSignatureScheme();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block;
|
||||
|
||||
import com.reandroid.arsc.io.BlockReader;
|
||||
import com.reandroid.arsc.item.ByteArray;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
// General purpose block to consume the specified bytes of BlockReader
|
||||
// TODO: No class should override this, implement all like SchemeV2
|
||||
public class UnknownScheme extends SignatureScheme{
|
||||
private final ByteArray byteArray;
|
||||
public UnknownScheme(SignatureId signatureId) {
|
||||
super(1, signatureId);
|
||||
this.byteArray = new ByteArray();
|
||||
addChild(byteArray);
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
SignatureInfo signatureInfo = getSignatureInfo();
|
||||
int size = (int) signatureInfo.getDataSize() - 4;
|
||||
byteArray.setSize(size);
|
||||
super.onReadBytes(reader);
|
||||
}
|
||||
}
|
@ -98,29 +98,4 @@ public abstract class ZipBlock extends BlockItem {
|
||||
putShort(getBytesInternal(), offset, (short) value);
|
||||
}
|
||||
|
||||
public static long getLong(byte[] bytes, int offset){
|
||||
if((offset + 8)>bytes.length){
|
||||
return 0;
|
||||
}
|
||||
long result = 0;
|
||||
int index = offset + 7;
|
||||
while (index>=offset){
|
||||
result = result << 8;
|
||||
result |= (bytes[index] & 0xff);
|
||||
index --;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public static void putLong(byte[] bytes, int offset, long value){
|
||||
if((offset + 8) > bytes.length){
|
||||
return;
|
||||
}
|
||||
int index = offset;
|
||||
offset = index + 8;
|
||||
while (index<offset){
|
||||
bytes[index] = (byte) (value & 0xff);
|
||||
value = value >>> 8;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block.pad;
|
||||
|
||||
import com.reandroid.archive2.block.SignatureId;
|
||||
import com.reandroid.archive2.block.SignatureInfo;
|
||||
import com.reandroid.archive2.block.SignatureScheme;
|
||||
import com.reandroid.arsc.io.BlockReader;
|
||||
import com.reandroid.arsc.item.ByteArray;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SchemePadding extends SignatureScheme {
|
||||
private final ByteArray byteArray;
|
||||
public SchemePadding() {
|
||||
super(1, SignatureId.PADDING);
|
||||
this.byteArray = new ByteArray();
|
||||
addChild(this.byteArray);
|
||||
}
|
||||
@Override
|
||||
public void onReadBytes(BlockReader reader) throws IOException{
|
||||
SignatureInfo signatureInfo = getSignatureInfo();
|
||||
int size = (int) signatureInfo.getDataSize() - 4;
|
||||
byteArray.setSize(size);
|
||||
super.onReadBytes(reader);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block.stamp;
|
||||
|
||||
import com.reandroid.archive2.block.SignatureId;
|
||||
import com.reandroid.archive2.block.UnknownScheme;
|
||||
|
||||
// TODO: implement structure
|
||||
public class SchemeStampV1 extends UnknownScheme {
|
||||
public SchemeStampV1() {
|
||||
super(SignatureId.STAMP_V1);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block.stamp;
|
||||
|
||||
import com.reandroid.archive2.block.SignatureId;
|
||||
import com.reandroid.archive2.block.UnknownScheme;
|
||||
|
||||
// TODO: implement structure
|
||||
public class SchemeStampV2 extends UnknownScheme {
|
||||
public SchemeStampV2() {
|
||||
super(SignatureId.STAMP_V2);
|
||||
}
|
||||
}
|
29
src/main/java/com/reandroid/archive2/block/v2/SchemeV2.java
Normal file
29
src/main/java/com/reandroid/archive2/block/v2/SchemeV2.java
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block.v2;
|
||||
|
||||
import com.reandroid.archive2.block.SignatureId;
|
||||
import com.reandroid.archive2.block.SignatureScheme;
|
||||
|
||||
public class SchemeV2 extends SignatureScheme {
|
||||
private final V2SignedDataList signedDataList;
|
||||
public SchemeV2(){
|
||||
super(1, SignatureId.V2);
|
||||
this.signedDataList = new V2SignedDataList();
|
||||
addChild(this.signedDataList);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block.v2;
|
||||
|
||||
import com.reandroid.archive2.block.LengthPrefixedBytes;
|
||||
|
||||
public class V2Signature extends LengthPrefixedBytes {
|
||||
public V2Signature() {
|
||||
super(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block.v2;
|
||||
|
||||
import com.reandroid.archive2.block.BottomBlock;
|
||||
import com.reandroid.archive2.block.LengthPrefixedBlock;
|
||||
import com.reandroid.arsc.io.BlockReader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class V2SignedData extends LengthPrefixedBlock {
|
||||
private final V2Signer signer;
|
||||
private final BottomBlock unknown;
|
||||
public V2SignedData() {
|
||||
super(2, false);
|
||||
this.signer = new V2Signer();
|
||||
this.unknown = new BottomBlock();
|
||||
addChild(this.signer);
|
||||
addChild(this.unknown);
|
||||
}
|
||||
public void onReadBytes(BlockReader reader) throws IOException {
|
||||
super.onReadBytes(reader);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block.v2;
|
||||
|
||||
import com.reandroid.archive2.block.LengthPrefixedList;
|
||||
|
||||
public class V2SignedDataList extends LengthPrefixedList<V2SignedData> {
|
||||
public V2SignedDataList() {
|
||||
super(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V2SignedData newInstance() {
|
||||
return new V2SignedData();
|
||||
}
|
||||
}
|
51
src/main/java/com/reandroid/archive2/block/v2/V2Signer.java
Normal file
51
src/main/java/com/reandroid/archive2/block/v2/V2Signer.java
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block.v2;
|
||||
|
||||
import com.reandroid.archive2.block.BottomBlock;
|
||||
import com.reandroid.archive2.block.CertificateBlock;
|
||||
import com.reandroid.archive2.block.CertificateBlockList;
|
||||
import com.reandroid.archive2.block.LengthPrefixedBlock;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class V2Signer extends LengthPrefixedBlock {
|
||||
private final V2Signature v2Signature;
|
||||
private final CertificateBlockList certificateBlockList;
|
||||
private final BottomBlock unknown;
|
||||
public V2Signer() {
|
||||
super(3, false);
|
||||
this.v2Signature = new V2Signature();
|
||||
this.certificateBlockList = new CertificateBlockList();
|
||||
this.unknown = new BottomBlock();
|
||||
addChild(this.v2Signature);
|
||||
addChild(this.certificateBlockList);
|
||||
addChild(this.unknown);
|
||||
}
|
||||
public List<CertificateBlock> getCertificateBlockList(){
|
||||
return certificateBlockList.getElements();
|
||||
}
|
||||
public void addCertificateBlock(CertificateBlock certificateBlock){
|
||||
certificateBlockList.add(certificateBlock);
|
||||
}
|
||||
public void removeCertificateBlock(CertificateBlock certificateBlock){
|
||||
certificateBlockList.remove(certificateBlock);
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return super.toString()+", sig="+v2Signature+", certs="+certificateBlockList;
|
||||
}
|
||||
}
|
26
src/main/java/com/reandroid/archive2/block/v3/SchemeV3.java
Normal file
26
src/main/java/com/reandroid/archive2/block/v3/SchemeV3.java
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block.v3;
|
||||
|
||||
import com.reandroid.archive2.block.SignatureId;
|
||||
import com.reandroid.archive2.block.UnknownScheme;
|
||||
|
||||
// TODO: implement structure
|
||||
public class SchemeV3 extends UnknownScheme {
|
||||
public SchemeV3() {
|
||||
super(SignatureId.V3);
|
||||
}
|
||||
}
|
26
src/main/java/com/reandroid/archive2/block/v3/SchemeV31.java
Normal file
26
src/main/java/com/reandroid/archive2/block/v3/SchemeV31.java
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.block.v3;
|
||||
|
||||
import com.reandroid.archive2.block.SignatureId;
|
||||
import com.reandroid.archive2.block.UnknownScheme;
|
||||
|
||||
// TODO: implement structure
|
||||
public class SchemeV31 extends UnknownScheme {
|
||||
public SchemeV31() {
|
||||
super(SignatureId.V31);
|
||||
}
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 github.com/REAndroid
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.reandroid.archive2.model;
|
||||
|
||||
import com.reandroid.archive2.block.ApkSignature;
|
||||
import com.reandroid.archive2.block.LongBlock;
|
||||
import com.reandroid.archive2.block.SignatureFooter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ApkSigBlock {
|
||||
private final SignatureFooter signatureFooter;
|
||||
private final LongBlock mSize;
|
||||
private final List<ApkSignature> mSignatures;
|
||||
public ApkSigBlock(SignatureFooter signatureFooter){
|
||||
this.signatureFooter = signatureFooter;
|
||||
this.mSize = new LongBlock();
|
||||
this.mSignatures = new ArrayList<>();
|
||||
}
|
||||
|
||||
public LongBlock getTotalSize(){
|
||||
return mSize;
|
||||
}
|
||||
public List<ApkSignature> getSignatures() {
|
||||
return mSignatures;
|
||||
}
|
||||
|
||||
public void readBytes(InputStream inputStream) throws IOException {
|
||||
mSize.readBytes(inputStream);
|
||||
ApkSignature apkSignature;
|
||||
while ((apkSignature = readNext(inputStream))!=null){
|
||||
mSignatures.add(apkSignature);
|
||||
}
|
||||
}
|
||||
private ApkSignature readNext(InputStream inputStream) throws IOException {
|
||||
ApkSignature apkSignature = new ApkSignature();
|
||||
apkSignature.readBytes(inputStream);
|
||||
if(apkSignature.isValid()){
|
||||
return apkSignature;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public void writeSigData(File dir) throws IOException{
|
||||
for(ApkSignature apkSignature:mSignatures){
|
||||
apkSignature.writeToDir(dir);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public String toString(){
|
||||
return "size=" + getTotalSize() + ", count=" + getSignatures().size();
|
||||
}
|
||||
|
||||
private static final long CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES = 1024 * 1024;
|
||||
public static final int ANDROID_COMMON_PAGE_ALIGNMENT_BYTES = 4096;
|
||||
|
||||
private static final byte[] APK_SIGNING_BLOCK_MAGIC = new byte[] {
|
||||
0x41, 0x50, 0x4b, 0x20, 0x53, 0x69, 0x67, 0x20,
|
||||
0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x34, 0x32,
|
||||
};
|
||||
public static final int VERITY_PADDING_BLOCK_ID = 0x42726577;
|
||||
|
||||
public static final int VERSION_SOURCE_STAMP = 0;
|
||||
public static final int VERSION_JAR_SIGNATURE_SCHEME = 1;
|
||||
public static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2;
|
||||
public static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3;
|
||||
public static final int VERSION_APK_SIGNATURE_SCHEME_V31 = 31;
|
||||
public static final int VERSION_APK_SIGNATURE_SCHEME_V4 = 4;
|
||||
|
||||
}
|
@ -16,7 +16,9 @@
|
||||
package com.reandroid.archive2.model;
|
||||
|
||||
import com.reandroid.archive2.block.*;
|
||||
import com.reandroid.archive2.block.ApkSignatureBlock;
|
||||
import com.reandroid.archive2.io.ZipSource;
|
||||
import com.reandroid.arsc.io.BlockReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -26,7 +28,7 @@ import java.util.List;
|
||||
public class LocalFileDirectory {
|
||||
private final CentralFileDirectory centralFileDirectory;
|
||||
private final List<LocalFileHeader> headerList;
|
||||
private ApkSigBlock apkSigBlock;
|
||||
private ApkSignatureBlock apkSignatureBlock;
|
||||
private long mTotalDataLength;
|
||||
public LocalFileDirectory(CentralFileDirectory centralFileDirectory){
|
||||
this.centralFileDirectory = centralFileDirectory;
|
||||
@ -86,15 +88,14 @@ public class LocalFileDirectory {
|
||||
return;
|
||||
}
|
||||
EndRecord endRecord = cfd.getEndRecord();
|
||||
long length = footer.getSigBlockSizeInFooter() + 8;
|
||||
long offset = endRecord.getOffsetOfCentralDirectory()
|
||||
- length;
|
||||
ApkSigBlock apkSigBlock = new ApkSigBlock(footer);
|
||||
apkSigBlock.readBytes(zipSource.getInputStream(offset, length));
|
||||
this.apkSigBlock = apkSigBlock;
|
||||
long length = footer.getSignatureSize() + 8;
|
||||
long offset = endRecord.getOffsetOfCentralDirectory() - length;
|
||||
ApkSignatureBlock apkSignatureBlock = new ApkSignatureBlock(footer);
|
||||
apkSignatureBlock.readBytes(new BlockReader(zipSource.getInputStream(offset, length)));
|
||||
this.apkSignatureBlock = apkSignatureBlock;
|
||||
}
|
||||
public ApkSigBlock getApkSigBlock() {
|
||||
return apkSigBlock;
|
||||
public ApkSignatureBlock getApkSigBlock() {
|
||||
return apkSignatureBlock;
|
||||
}
|
||||
public CentralFileDirectory getCentralFileDirectory() {
|
||||
return centralFileDirectory;
|
||||
|
Loading…
x
Reference in New Issue
Block a user