From 32a82f952703361de6aa5ebd62bcbfc80efdccd3 Mon Sep 17 00:00:00 2001 From: REAndroid Date: Mon, 10 Apr 2023 17:30:05 +0200 Subject: [PATCH] implement basic structure of APK signature block --- .../java/com/reandroid/archive2/Archive.java | 21 +- .../archive2/block/ApkSignature.java | 202 ------------------ .../archive2/block/ApkSignatureBlock.java | 78 +++++++ .../reandroid/archive2/block/BottomBlock.java | 36 ++++ .../archive2/block/CertificateBlock.java | 54 +++++ .../archive2/block/CertificateBlockList.java | 28 +++ .../archive2/block/LengthPrefixedBlock.java | 89 ++++++++ .../archive2/block/LengthPrefixedBytes.java | 35 +++ .../archive2/block/LengthPrefixedList.java | 125 +++++++++++ .../archive2/block/SignatureFooter.java | 20 +- .../reandroid/archive2/block/SignatureId.java | 109 ++++++++++ .../archive2/block/SignatureInfo.java | 111 ++++++++++ .../archive2/block/UnknownScheme.java | 39 ++++ .../reandroid/archive2/block/ZipBlock.java | 25 --- .../archive2/block/pad/SchemePadding.java | 40 ++++ .../archive2/block/stamp/SchemeStampV1.java | 26 +++ .../archive2/block/stamp/SchemeStampV2.java | 26 +++ .../reandroid/archive2/block/v2/SchemeV2.java | 29 +++ .../archive2/block/v2/V2Signature.java | 24 +++ .../archive2/block/v2/V2SignedData.java | 38 ++++ .../archive2/block/v2/V2SignedDataList.java | 29 +++ .../reandroid/archive2/block/v2/V2Signer.java | 51 +++++ .../reandroid/archive2/block/v3/SchemeV3.java | 26 +++ .../archive2/block/v3/SchemeV31.java | 26 +++ .../reandroid/archive2/model/ApkSigBlock.java | 86 -------- .../archive2/model/LocalFileDirectory.java | 19 +- 26 files changed, 1058 insertions(+), 334 deletions(-) delete mode 100644 src/main/java/com/reandroid/archive2/block/ApkSignature.java create mode 100644 src/main/java/com/reandroid/archive2/block/ApkSignatureBlock.java create mode 100644 src/main/java/com/reandroid/archive2/block/BottomBlock.java create mode 100644 src/main/java/com/reandroid/archive2/block/CertificateBlock.java create mode 100644 src/main/java/com/reandroid/archive2/block/CertificateBlockList.java create mode 100644 src/main/java/com/reandroid/archive2/block/LengthPrefixedBlock.java create mode 100644 src/main/java/com/reandroid/archive2/block/LengthPrefixedBytes.java create mode 100644 src/main/java/com/reandroid/archive2/block/LengthPrefixedList.java create mode 100644 src/main/java/com/reandroid/archive2/block/SignatureId.java create mode 100644 src/main/java/com/reandroid/archive2/block/SignatureInfo.java create mode 100644 src/main/java/com/reandroid/archive2/block/UnknownScheme.java create mode 100644 src/main/java/com/reandroid/archive2/block/pad/SchemePadding.java create mode 100644 src/main/java/com/reandroid/archive2/block/stamp/SchemeStampV1.java create mode 100644 src/main/java/com/reandroid/archive2/block/stamp/SchemeStampV2.java create mode 100644 src/main/java/com/reandroid/archive2/block/v2/SchemeV2.java create mode 100644 src/main/java/com/reandroid/archive2/block/v2/V2Signature.java create mode 100644 src/main/java/com/reandroid/archive2/block/v2/V2SignedData.java create mode 100644 src/main/java/com/reandroid/archive2/block/v2/V2SignedDataList.java create mode 100644 src/main/java/com/reandroid/archive2/block/v2/V2Signer.java create mode 100644 src/main/java/com/reandroid/archive2/block/v3/SchemeV3.java create mode 100644 src/main/java/com/reandroid/archive2/block/v3/SchemeV31.java delete mode 100644 src/main/java/com/reandroid/archive2/model/ApkSigBlock.java diff --git a/src/main/java/com/reandroid/archive2/Archive.java b/src/main/java/com/reandroid/archive2/Archive.java index a00e02a..9942edb 100644 --- a/src/main/java/com/reandroid/archive2/Archive.java +++ b/src/main/java/com/reandroid/archive2/Archive.java @@ -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 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); + } } diff --git a/src/main/java/com/reandroid/archive2/block/ApkSignature.java b/src/main/java/com/reandroid/archive2/block/ApkSignature.java deleted file mode 100644 index 13d0861..0000000 --- a/src/main/java/com/reandroid/archive2/block/ApkSignature.java +++ /dev/null @@ -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; -} diff --git a/src/main/java/com/reandroid/archive2/block/ApkSignatureBlock.java b/src/main/java/com/reandroid/archive2/block/ApkSignatureBlock.java new file mode 100644 index 0000000..0cfd348 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/ApkSignatureBlock.java @@ -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 { + public ApkSignatureBlock(SignatureFooter signatureFooter){ + super(true); + setBottomBlock(signatureFooter); + } + public List 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; + +} diff --git a/src/main/java/com/reandroid/archive2/block/BottomBlock.java b/src/main/java/com/reandroid/archive2/block/BottomBlock.java new file mode 100644 index 0000000..2ae5aea --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/BottomBlock.java @@ -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 { + 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); + } + } +} diff --git a/src/main/java/com/reandroid/archive2/block/CertificateBlock.java b/src/main/java/com/reandroid/archive2/block/CertificateBlock.java new file mode 100644 index 0000000..1894f70 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/CertificateBlock.java @@ -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; +} diff --git a/src/main/java/com/reandroid/archive2/block/CertificateBlockList.java b/src/main/java/com/reandroid/archive2/block/CertificateBlockList.java new file mode 100644 index 0000000..958cf13 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/CertificateBlockList.java @@ -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{ + public CertificateBlockList() { + super(false); + } + @Override + public CertificateBlock newInstance() { + return new CertificateBlock(); + } + +} diff --git a/src/main/java/com/reandroid/archive2/block/LengthPrefixedBlock.java b/src/main/java/com/reandroid/archive2/block/LengthPrefixedBlock.java new file mode 100644 index 0000000..014f389 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/LengthPrefixedBlock.java @@ -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 extends FixedBlockContainer + implements BlockCreator { + private final Block numberBlock; + private final BlockList elements; + private final SingleBlockContainer 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 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(); + } +} diff --git a/src/main/java/com/reandroid/archive2/block/SignatureFooter.java b/src/main/java/com/reandroid/archive2/block/SignatureFooter.java index 25c94f3..3594021 100644 --- a/src/main/java/com/reandroid/archive2/block/SignatureFooter.java +++ b/src/main/java/com/reandroid/archive2/block/SignatureFooter.java @@ -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; diff --git a/src/main/java/com/reandroid/archive2/block/SignatureId.java b/src/main/java/com/reandroid/archive2/block/SignatureId.java new file mode 100644 index 0000000..616e7be --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/SignatureId.java @@ -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"; +} diff --git a/src/main/java/com/reandroid/archive2/block/SignatureInfo.java b/src/main/java/com/reandroid/archive2/block/SignatureInfo.java new file mode 100644 index 0000000..4451961 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/SignatureInfo.java @@ -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 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(); + } + +} diff --git a/src/main/java/com/reandroid/archive2/block/UnknownScheme.java b/src/main/java/com/reandroid/archive2/block/UnknownScheme.java new file mode 100644 index 0000000..172f8e4 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/UnknownScheme.java @@ -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); + } +} diff --git a/src/main/java/com/reandroid/archive2/block/ZipBlock.java b/src/main/java/com/reandroid/archive2/block/ZipBlock.java index 33e4ddb..8669aad 100644 --- a/src/main/java/com/reandroid/archive2/block/ZipBlock.java +++ b/src/main/java/com/reandroid/archive2/block/ZipBlock.java @@ -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>> 8; - index++; - } - } } diff --git a/src/main/java/com/reandroid/archive2/block/pad/SchemePadding.java b/src/main/java/com/reandroid/archive2/block/pad/SchemePadding.java new file mode 100644 index 0000000..b088c18 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/pad/SchemePadding.java @@ -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); + } +} diff --git a/src/main/java/com/reandroid/archive2/block/stamp/SchemeStampV1.java b/src/main/java/com/reandroid/archive2/block/stamp/SchemeStampV1.java new file mode 100644 index 0000000..aa2b4f3 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/stamp/SchemeStampV1.java @@ -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); + } +} diff --git a/src/main/java/com/reandroid/archive2/block/stamp/SchemeStampV2.java b/src/main/java/com/reandroid/archive2/block/stamp/SchemeStampV2.java new file mode 100644 index 0000000..ceb8504 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/stamp/SchemeStampV2.java @@ -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); + } +} diff --git a/src/main/java/com/reandroid/archive2/block/v2/SchemeV2.java b/src/main/java/com/reandroid/archive2/block/v2/SchemeV2.java new file mode 100644 index 0000000..c7e4c23 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/v2/SchemeV2.java @@ -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); + } + +} diff --git a/src/main/java/com/reandroid/archive2/block/v2/V2Signature.java b/src/main/java/com/reandroid/archive2/block/v2/V2Signature.java new file mode 100644 index 0000000..f73ee33 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/v2/V2Signature.java @@ -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); + } +} diff --git a/src/main/java/com/reandroid/archive2/block/v2/V2SignedData.java b/src/main/java/com/reandroid/archive2/block/v2/V2SignedData.java new file mode 100644 index 0000000..8eb53f1 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/v2/V2SignedData.java @@ -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); + } + +} diff --git a/src/main/java/com/reandroid/archive2/block/v2/V2SignedDataList.java b/src/main/java/com/reandroid/archive2/block/v2/V2SignedDataList.java new file mode 100644 index 0000000..5767ea3 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/v2/V2SignedDataList.java @@ -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 { + public V2SignedDataList() { + super(false); + } + + @Override + public V2SignedData newInstance() { + return new V2SignedData(); + } +} diff --git a/src/main/java/com/reandroid/archive2/block/v2/V2Signer.java b/src/main/java/com/reandroid/archive2/block/v2/V2Signer.java new file mode 100644 index 0000000..cc99578 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/v2/V2Signer.java @@ -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 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; + } +} diff --git a/src/main/java/com/reandroid/archive2/block/v3/SchemeV3.java b/src/main/java/com/reandroid/archive2/block/v3/SchemeV3.java new file mode 100644 index 0000000..5b9c9d8 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/v3/SchemeV3.java @@ -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); + } +} diff --git a/src/main/java/com/reandroid/archive2/block/v3/SchemeV31.java b/src/main/java/com/reandroid/archive2/block/v3/SchemeV31.java new file mode 100644 index 0000000..4043348 --- /dev/null +++ b/src/main/java/com/reandroid/archive2/block/v3/SchemeV31.java @@ -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); + } +} diff --git a/src/main/java/com/reandroid/archive2/model/ApkSigBlock.java b/src/main/java/com/reandroid/archive2/model/ApkSigBlock.java deleted file mode 100644 index 7a429bd..0000000 --- a/src/main/java/com/reandroid/archive2/model/ApkSigBlock.java +++ /dev/null @@ -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 mSignatures; - public ApkSigBlock(SignatureFooter signatureFooter){ - this.signatureFooter = signatureFooter; - this.mSize = new LongBlock(); - this.mSignatures = new ArrayList<>(); - } - - public LongBlock getTotalSize(){ - return mSize; - } - public List 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; - -} diff --git a/src/main/java/com/reandroid/archive2/model/LocalFileDirectory.java b/src/main/java/com/reandroid/archive2/model/LocalFileDirectory.java index ef4a542..23375e7 100644 --- a/src/main/java/com/reandroid/archive2/model/LocalFileDirectory.java +++ b/src/main/java/com/reandroid/archive2/model/LocalFileDirectory.java @@ -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 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;