diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/Extension.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/Extension.java index 2d23c548812465c6419fea07d40299268c9121cb..6adad959e5cb30372dea9786dbb3e2087a3b6303 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/Extension.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/Extension.java @@ -38,7 +38,7 @@ public class Extension { private final int type; - private final int size; + private int size; public Extension(int type, int size) { this.type = type; @@ -49,6 +49,10 @@ public class Extension { return EXTENSION_HEADER_SIZE; } + public void setSize(int size) { + this.size = size; + } + public boolean isType(int type) { return this.type == type; } diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/HapInfoSegment.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/HapInfoSegment.java index 2697ea7af826915595ec20b941f6dec06f82f964..4b8be5854db2131bf50cd080b89d63777068e7a3 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/HapInfoSegment.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/HapInfoSegment.java @@ -121,7 +121,7 @@ public class HapInfoSegment { String.format(Locale.ROOT, "Invalid dataSize number of HapInfoSegment, not a multiple of 4096: %d", inHapSignInfo.getDataSize())); } - if (inHapSignInfo.getExtensionNum() != SignInfo.MAX_EXTENSION_NUM) { + if (inHapSignInfo.getExtensionNum() > SignInfo.MAX_EXTENSION_NUM) { throw new VerifyCodeSignException("Invalid extensionNum of HapInfoSegment"); } if (inHapSignInfo.getExtensionByType(MerkleTreeExtension.MERKLE_TREE_INLINED) == null) { diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/MerkleTreeExtension.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/MerkleTreeExtension.java index 303d92edf13effedc56dec7605e4b2c3bd5aa908..39840619ea14c257f01ddb5d64d23dc406651cde 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/MerkleTreeExtension.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/MerkleTreeExtension.java @@ -64,7 +64,7 @@ public class MerkleTreeExtension extends Extension { * @param rootHash Root hash of the merkle tree */ public MerkleTreeExtension(long merkleTreeSize, long merkleTreeOffset, byte[] rootHash) { - super(MERKLE_TREE_INLINED, MERKLE_TREE_EXTENSION_DATA_SIZE); + super(MERKLE_TREE_INLINED, Extension.EXTENSION_HEADER_SIZE + MERKLE_TREE_EXTENSION_DATA_SIZE); this.merkleTreeSize = merkleTreeSize; this.merkleTreeOffset = merkleTreeOffset; if (rootHash == null) { @@ -98,8 +98,7 @@ public class MerkleTreeExtension extends Extension { */ @Override public byte[] toByteArray() { - ByteBuffer bf = ByteBuffer.allocate(Extension.EXTENSION_HEADER_SIZE + MERKLE_TREE_EXTENSION_DATA_SIZE) - .order(ByteOrder.LITTLE_ENDIAN); + ByteBuffer bf = ByteBuffer.allocate(size()).order(ByteOrder.LITTLE_ENDIAN); bf.put(super.toByteArray()); bf.putLong(this.merkleTreeSize); bf.putLong(this.merkleTreeOffset); diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/PageInfoExtension.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/PageInfoExtension.java new file mode 100644 index 0000000000000000000000000000000000000000..4dae86b515b022056bad303accfda1ede6150864 --- /dev/null +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/PageInfoExtension.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * 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.ohos.hapsigntool.codesigning.datastructure; + +import com.ohos.hapsigntool.codesigning.exception.VerifyCodeSignException; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Pages info extension is a type of Extension to store bitmap file's information, i.e. size and offset, ect. + *

+ * structure + *

+ * 1) u32 type 0x2 + *

+ * 2) u64 mapOffset: offset of the bitmap by the start of the file. + *

+ * 3) u64 mapSize: the bit size of bitmap. + *

+ * 4) u8 unitSize: unit size corresponding to each page, default 4 . + *

+ * 5) u8[3] reserved: + *

+ * 6) u32 signSize: signature size + *

+ * 7) u8[] signature: signature of the data + * + * @since 2024/07/01 + */ +public class PageInfoExtension extends Extension { + /** + * Type of PageInfoExtension + */ + public static final int PAGE_INFO_INLINED = 0x2; + + /** + * Byte size of PageInfoExtension + */ + public static final int PAGE_INFO_EXTENSION_DATA_SIZE_WITHOUT_SIGN = 24; + + /** + * default unit size + */ + public static final int DEFAULT_UNIT_SIZE = 4; + + private static final int RESERVED_SIZE = 3; + + private static final int SIGNATURE_ALIGNMENT = 4; + + private long mapOffset; + + private long mapSize; + + private byte unitSize; + + private byte[] reserved = new byte[RESERVED_SIZE]; + + private int signSize; + + private byte[] signature = new byte[0]; + + private byte[] zeroPadding = new byte[0]; + + /** + * Constructor for PageInfoExtension + * + * @param mapOffset bitmap offset + * @param mapSize bit size + */ + public PageInfoExtension(long mapOffset, long mapSize) { + super(PAGE_INFO_INLINED, Extension.EXTENSION_HEADER_SIZE + PAGE_INFO_EXTENSION_DATA_SIZE_WITHOUT_SIGN); + this.mapOffset = mapOffset; + this.mapSize = mapSize; + unitSize = DEFAULT_UNIT_SIZE; + } + + public void setSignature(byte[] signature) { + if (signature != null) { + this.signSize = signature.length; + this.signature = signature; + this.zeroPadding = new byte[(SIGNATURE_ALIGNMENT - (signSize % SIGNATURE_ALIGNMENT)) % SIGNATURE_ALIGNMENT]; + } + super.setSize(size()); + } + + public long getMapOffset() { + return mapOffset; + } + + public long getMapSize() { + return mapSize; + } + + public byte getUnitSize() { + return unitSize; + } + + /** + * Byte size of PageInfoExtension + * + * @return Byte size of PageInfoExtension + */ + @Override + public int size() { + return Extension.EXTENSION_HEADER_SIZE + PAGE_INFO_EXTENSION_DATA_SIZE_WITHOUT_SIGN + signSize + + zeroPadding.length; + } + + /** + * Converts PageInfoExtension to a newly created byte array + * + * @return Byte array representation of PageInfoExtension + */ + @Override + public byte[] toByteArray() { + ByteBuffer bf = ByteBuffer.allocate(size()).order(ByteOrder.LITTLE_ENDIAN); + bf.put(super.toByteArray()); + bf.putLong(this.mapOffset); + bf.putLong(this.mapSize); + bf.put(this.unitSize); + bf.put(this.reserved); + bf.putInt(this.signSize); + bf.put(this.signature); + bf.put(this.zeroPadding); + return bf.array(); + } + + /** + * Init the PageInfoExtension by a byte array + * + * @param bytes Byte array representation of a PageInfoExtension object + * @return a newly created PageInfoExtension object + * @throws VerifyCodeSignException parse result invalid + */ + public static PageInfoExtension fromByteArray(byte[] bytes) throws VerifyCodeSignException { + ByteBuffer bf = ByteBuffer.allocate(bytes.length).order(ByteOrder.LITTLE_ENDIAN); + bf.put(bytes); + bf.rewind(); + long inMapOffset = bf.getLong(); + if (inMapOffset % CodeSignBlock.PAGE_SIZE_4K != 0) { + throw new VerifyCodeSignException("mapOffset is not a multiple of 4096"); + } + long inMapSize = bf.getLong(); + byte inUnitSize = bf.get(); + if (inMapSize % inUnitSize != 0) { + throw new VerifyCodeSignException("mapSize is not a multiple of unitSize"); + } + bf.get(new byte[RESERVED_SIZE]); + int inSignSize = bf.getInt(); + byte[] inSignature = new byte[inSignSize]; + bf.get(inSignature); + PageInfoExtension extension = new PageInfoExtension(inMapOffset, inMapSize); + extension.unitSize = inUnitSize; + extension.setSignature(inSignature); + return extension; + } +} diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/SignInfo.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/SignInfo.java index e2d538c4f3dc2cd6d05518caf9240c7be54b9d50..d146bf8f3194ad8f3aa277e94427b526d8053282 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/SignInfo.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/datastructure/SignInfo.java @@ -58,7 +58,7 @@ public class SignInfo { /** * maximum of extension number */ - public static final int MAX_EXTENSION_NUM = 1; + public static final int MAX_EXTENSION_NUM = 2; /** * sign info structure without signature in bytes, refer to toByteArray() method @@ -112,6 +112,7 @@ public class SignInfo { this.sigSize = sig == null ? 0 : sig.length; // align for extension after signature this.zeroPadding = new byte[(SIGNATURE_ALIGNMENT - (this.sigSize % SIGNATURE_ALIGNMENT)) % SIGNATURE_ALIGNMENT]; + this.extensionOffset = SIGN_INFO_SIZE_WITHOUT_SIGNATURE + sigSize + this.zeroPadding.length; } /** @@ -138,7 +139,6 @@ public class SignInfo { * @param extension Extension object */ public void addExtension(Extension extension) { - this.extensionOffset = this.size(); this.extensionList.add(extension); this.extensionNum = this.extensionList.size(); } @@ -254,7 +254,7 @@ public class SignInfo { % SIGNATURE_ALIGNMENT]; bf.get(inZeroPadding); // parse merkle tree extension - List inExtensionList = parseMerkleTreeExtension(bf, inExtensionNum); + List inExtensionList = parseExtensionList(bf, inExtensionNum); return new SignInfoBuilder().setSaltSize(inSaltSize) .setSigSize(inSigSize) .setFlags(inFlags) @@ -268,22 +268,33 @@ public class SignInfo { .build(); } - private static List parseMerkleTreeExtension(ByteBuffer bf, int inExtensionNum) + private static List parseExtensionList(ByteBuffer bf, int inExtensionNum) throws VerifyCodeSignException { List inExtensionList = new ArrayList<>(); - if (inExtensionNum == 1) { - // parse merkle tree extension + for (int i = 0; i < inExtensionNum; i++) { int extensionType = bf.getInt(); - if (extensionType != MerkleTreeExtension.MERKLE_TREE_INLINED) { + if (extensionType == MerkleTreeExtension.MERKLE_TREE_INLINED) { + // parse merkle tree extension + int extensionSize = bf.getInt(); + if (extensionSize != (Extension.EXTENSION_HEADER_SIZE + + MerkleTreeExtension.MERKLE_TREE_EXTENSION_DATA_SIZE)) { + throw new VerifyCodeSignException("Invalid extensionSize of SignInfo"); + } + byte[] merkleTreeExtension = new byte[MerkleTreeExtension.MERKLE_TREE_EXTENSION_DATA_SIZE]; + bf.get(merkleTreeExtension); + inExtensionList.add(MerkleTreeExtension.fromByteArray(merkleTreeExtension)); + } else if (extensionType == PageInfoExtension.PAGE_INFO_INLINED) { + // parse page info extension + int extensionSize = bf.getInt(); + if (extensionSize <= (Extension.EXTENSION_HEADER_SIZE)) { + throw new VerifyCodeSignException("Invalid extensionSize of SignInfo"); + } + byte[] pageInfoExtension = new byte[extensionSize - Extension.EXTENSION_HEADER_SIZE]; + bf.get(pageInfoExtension); + inExtensionList.add(PageInfoExtension.fromByteArray(pageInfoExtension)); + } else { throw new VerifyCodeSignException("Invalid extensionType of SignInfo"); } - int extensionSize = bf.getInt(); - if (extensionSize != MerkleTreeExtension.MERKLE_TREE_EXTENSION_DATA_SIZE) { - throw new VerifyCodeSignException("Invalid extensionSize of SignInfo"); - } - byte[] merkleTreeExtension = new byte[MerkleTreeExtension.MERKLE_TREE_EXTENSION_DATA_SIZE]; - bf.get(merkleTreeExtension); - inExtensionList.add(MerkleTreeExtension.fromByteArray(merkleTreeExtension)); } return inExtensionList; } diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/elf/ElfDefine.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/elf/ElfDefine.java new file mode 100644 index 0000000000000000000000000000000000000000..288d5daeec73c3d735d6f4c6bc17c8baab741c20 --- /dev/null +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/elf/ElfDefine.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * 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.ohos.hapsigntool.codesigning.elf; + +/** + * ELF struct define + * + * @since 2024/07/01 + */ +public interface ElfDefine { + /** + * 32-bit elf file + */ + byte ELF_32_CLASS = 1; + + /** + * 64-bit elf file + */ + byte ELF_64_CLASS = 2; + + /** + * little endian + */ + byte ELF_DATA_2_LSB = 1; + + /** + * big endian + */ + byte ELF_DATA_2_MSB = 2; + + /** + * 32-bit elf file's program header length + */ + int ELF_PHEADER_32_LEN = 32; + + /** + * 64-bit elf file's program header length + */ + int ELF_PHEADER_64_LEN = 56; + + /** + * elf header e_ident length + */ + int EI_NIDENT_LEN = 16; + + /** + * 32-bit elf header length + */ + int ELF_HEADER_32_LEN = EI_NIDENT_LEN + 36; + + /** + * 64-bit elf header length + */ + int ELF_HEADER_64_LEN = EI_NIDENT_LEN + 48; +} diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/elf/ElfFile.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/elf/ElfFile.java new file mode 100644 index 0000000000000000000000000000000000000000..e84d446c2e2ac0b3b74f2e232a1637d579d2f472 --- /dev/null +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/elf/ElfFile.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * 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.ohos.hapsigntool.codesigning.elf; + +import com.ohos.hapsigntool.codesigning.exception.ElfFormatException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * ELF file + * + * @since 2024/07/01 + */ +public class ElfFile { + private ElfHeader elfHeader; + + private final List programHeaderList = new ArrayList<>(); + + /** + * Constructor for ElfFile + * + * @param is InputStream + * @throws IOException io error + * @throws ElfFormatException elf file format error + */ + public ElfFile(InputStream is) throws IOException, ElfFormatException { + elfHeader = new ElfHeader(is); + byte eiClass = elfHeader.getEiClass(); + byte eiData = elfHeader.getEiData(); + short ePhnum = elfHeader.getEPhnum(); + long ePhOff = elfHeader.getEPhOff(); + if (eiClass == ElfDefine.ELF_32_CLASS) { + is.skip(ePhOff - ElfDefine.ELF_HEADER_32_LEN); + } else if (eiClass == ElfDefine.ELF_64_CLASS) { + is.skip(ePhOff - ElfDefine.ELF_HEADER_64_LEN); + } + for (short i = 0; i < ePhnum; i++) { + ElfProgramHeader pHeader = new ElfProgramHeader(is, eiClass, eiData); + programHeaderList.add(pHeader); + } + } + + /** + * filter executable program segment headers + * + * @return executable program segment headers + */ + public List filterExecPHeaders() { + return programHeaderList.stream().filter(phdr -> (phdr.getPFlags() & 1) == 1).collect(Collectors.toList()); + } +} diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/elf/ElfHeader.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/elf/ElfHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..681a754c83b15e47fca79296de2f738b0b28f4f4 --- /dev/null +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/elf/ElfHeader.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * 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.ohos.hapsigntool.codesigning.elf; + +import com.ohos.hapsigntool.codesigning.exception.ElfFormatException; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * ELF header info + * + * @since 2024/07/01 + */ +public class ElfHeader { + /** + * Magic number and other info + */ + private byte[] ident = new byte[ElfDefine.EI_NIDENT_LEN]; + + /** + * 32-bit or 64-bit file + */ + private byte eiClass; + + /** + * LITTLE_ENDIAN or BIG_ENDIAN + */ + private byte eiData; + + /** + * elf version + */ + private byte eiVersion; + + /** + * Object file type + */ + private short eType; + + /** + * Architecture + */ + private int eMachine; + + /** + * Object file version + */ + private int eVersion; + + /** + * Entry point virtual address + */ + private long eEntry; + + /** + * Program header table file offset + */ + private long ePhOff; + + /** + * Section header table file offset + */ + private long eShOff; + + /** + * Processor-specific flags + */ + private int eFlags; + + /** + * ELF header size in bytes + */ + private short eEhSize; + + /** + * Program header table entry size + */ + private short ePhEntSize; + + /** + * Program header table entry count + */ + private short ePhNum; + + /** + * Section header table entry size + */ + private short eShEntSize; + + /** + * Section header table entry count + */ + private short eShNum; + + /** + * Section header string table index + */ + private short eShStrndx; + + /** + * Constructor for ElfHeader + * + * @param is InputStream + * @throws IOException io error + * @throws ElfFormatException elf file format error + */ + public ElfHeader(InputStream is) throws IOException, ElfFormatException { + int read = is.read(ident); + if (read != ident.length || !(ident[0] == 0x7F && ident[1] == 0x45 && ident[2] == 0x4C && ident[3] == 0x46)) { + throw new ElfFormatException("Not a elf file"); + } + eiClass = ident[4]; + eiData = ident[5]; + eiVersion = ident[6]; + if (eiVersion < 1) { + throw new ElfFormatException("ELF eiVersion is incorrect"); + } + int len; + if (eiClass == ElfDefine.ELF_32_CLASS) { + len = ElfDefine.ELF_HEADER_32_LEN - ElfDefine.EI_NIDENT_LEN; + } else if (eiClass == ElfDefine.ELF_64_CLASS) { + len = ElfDefine.ELF_HEADER_64_LEN - ElfDefine.EI_NIDENT_LEN; + } else { + throw new ElfFormatException("ELF eiClass is incorrect"); + } + ByteOrder bo; + if (eiData == ElfDefine.ELF_DATA_2_LSB) { + bo = ByteOrder.LITTLE_ENDIAN; + } else if (eiData == ElfDefine.ELF_DATA_2_MSB) { + bo = ByteOrder.BIG_ENDIAN; + } else { + throw new ElfFormatException("ELF eiData is incorrect"); + } + byte[] bytes = new byte[len]; + read = is.read(bytes); + if (read != len) { + throw new ElfFormatException("ELF file header is incorrect"); + } + ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + byteBuffer.order(bo); + initHeader(byteBuffer); + } + + private void initHeader(ByteBuffer byteBuffer) throws ElfFormatException { + eType = byteBuffer.getShort(); + eMachine = byteBuffer.getShort(); + eVersion = byteBuffer.getInt(); + + if (eiClass == ElfDefine.ELF_32_CLASS) { + eEntry = byteBuffer.getInt() & 0xFFFFFFFFL; + ePhOff = byteBuffer.getInt() & 0xFFFFFFFFL; + eShOff = byteBuffer.getInt() & 0xFFFFFFFFL; + if (ePhOff != ElfDefine.ELF_HEADER_32_LEN) { + throw new ElfFormatException("ELF Program header table file offset is incorrect"); + } + } else { + eEntry = byteBuffer.getLong(); + ePhOff = byteBuffer.getLong(); + eShOff = byteBuffer.getLong(); + if (ePhOff != ElfDefine.ELF_HEADER_64_LEN) { + throw new ElfFormatException("ELF Program header table file offset is incorrect"); + } + } + eFlags = byteBuffer.getInt(); + eEhSize = byteBuffer.getShort(); + ePhEntSize = byteBuffer.getShort(); + ePhNum = byteBuffer.getShort(); + eShEntSize = byteBuffer.getShort(); + eShNum = byteBuffer.getShort(); + eShStrndx = byteBuffer.getShort(); + } + + public byte getEiClass() { + return eiClass; + } + + public byte getEiData() { + return eiData; + } + + public long getEPhOff() { + return ePhOff; + } + + public short getEPhnum() { + return ePhNum; + } +} diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/elf/ElfProgramHeader.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/elf/ElfProgramHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..c1b3ffa7bfd2e34ed3358fa89946e7af7a83aeba --- /dev/null +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/elf/ElfProgramHeader.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * 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.ohos.hapsigntool.codesigning.elf; + +import com.ohos.hapsigntool.codesigning.exception.ElfFormatException; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * ELF program header info + * + * @since 2024/07/01 + */ +public class ElfProgramHeader { + /** + * Segment type + */ + private int pType; + + /** + * Segment flags + */ + private int pFlags; + + /** + * Segment file offset + */ + private long pOffset; + + /** + * Segment virtual address + */ + private long pVaddr; + + /** + * Segment physical address + */ + private long pPaddr; + + /** + * Segment size in file + */ + private long pFilesz; + + /** + * Segment size in memory + */ + private long pMemsz; + + /** + * Segment alignment + */ + private long pAlign; + + /** + * Constructor for ElfPHeader + * + * @param is InputStream + * @param eiClass eiClass + * @param eiData eiData + * @throws IOException io error + * @throws ElfFormatException elf file format error + */ + public ElfProgramHeader(InputStream is, byte eiClass, byte eiData) throws IOException, ElfFormatException { + ByteOrder bo = ByteOrder.LITTLE_ENDIAN; + if (eiData == ElfDefine.ELF_DATA_2_LSB) { + bo = ByteOrder.LITTLE_ENDIAN; + } else if (eiData == ElfDefine.ELF_DATA_2_MSB) { + bo = ByteOrder.BIG_ENDIAN; + } else { + throw new ElfFormatException("ELF ei_data is incorrect"); + } + if (eiClass == ElfDefine.ELF_32_CLASS) { + byte[] bytes = new byte[ElfDefine.ELF_PHEADER_32_LEN]; + int read = is.read(bytes); + if (read != ElfDefine.ELF_PHEADER_32_LEN) { + throw new ElfFormatException("ELF program header is incorrect"); + } + ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + byteBuffer.order(bo); + pType = byteBuffer.getInt(); + pOffset = byteBuffer.getInt() & 0xFFFFFFFFL; + pVaddr = byteBuffer.getInt() & 0xFFFFFFFFL; + pPaddr = byteBuffer.getInt() & 0xFFFFFFFFL; + pFilesz = byteBuffer.getInt(); + pMemsz = byteBuffer.getInt(); + pFlags = byteBuffer.getInt(); + pAlign = byteBuffer.getInt(); + } else { + byte[] bytes = new byte[ElfDefine.ELF_PHEADER_64_LEN]; + int read = is.read(bytes); + if (read != ElfDefine.ELF_PHEADER_64_LEN) { + throw new ElfFormatException("ELF program header is incorrect"); + } + ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + byteBuffer.order(bo); + pType = byteBuffer.getInt(); + pFlags = byteBuffer.getInt(); + pOffset = byteBuffer.getLong(); + pVaddr = byteBuffer.getLong(); + pPaddr = byteBuffer.getLong(); + pFilesz = byteBuffer.getLong(); + pMemsz = byteBuffer.getLong(); + pAlign = byteBuffer.getLong(); + } + } + + public int getPFlags() { + return pFlags; + } + + public long getPOffset() { + return pOffset; + } + + public long getPFilesz() { + return pFilesz; + } +} diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/exception/ElfFormatException.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/exception/ElfFormatException.java new file mode 100644 index 0000000000000000000000000000000000000000..1089f073a2aea8458ca2a21463947d908344ed77 --- /dev/null +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/exception/ElfFormatException.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * 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.ohos.hapsigntool.codesigning.exception; + +/** + * Elf format exception + * + * @since 2024/07/01 + */ +public class ElfFormatException extends Exception { + public ElfFormatException(String message) { + super(message); + } + + public ElfFormatException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/fsverity/FsVerityDescriptor.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/fsverity/FsVerityDescriptor.java index 5531b7f837b0b55054bc7a1db9291d2e94f38916..2d3d044e62ff5a0b2a6122791892721a5b5c1681 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/fsverity/FsVerityDescriptor.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/fsverity/FsVerityDescriptor.java @@ -65,6 +65,11 @@ public class FsVerityDescriptor { */ public static final byte CODE_SIGN_VERSION = 0x1; + /** + * code sign version + */ + public static final byte CODE_SIGN_VERSION_V2 = 0x2; + /** * FsVerity descriptor size */ @@ -83,12 +88,7 @@ public class FsVerityDescriptor { /** * reserved size */ - public static final int RESERVED_SIZE_AFTER_FLAGS = 4; - - /** - * reserved size - */ - public static final int RESERVED_SIZE_AFTER_TREE_OFFSET = 127; + public static final int RESERVED_SIZE_AFTER_TREE_OFFSET = 119; private byte version; @@ -108,8 +108,12 @@ public class FsVerityDescriptor { private int flags; + private int bitMapSize; + private long merkleTreeOffset; + private long bitMapOffset; + private byte csVersion; private FsVerityDescriptor(Builder builder) { @@ -152,7 +156,7 @@ public class FsVerityDescriptor { bf.rewind(); FsVerityDescriptor.Builder builder = new FsVerityDescriptor.Builder(); byte inFsVersion = bf.get(); - if (FsVerityDescriptor.VERSION != inFsVersion) { + if (inFsVersion != FsVerityDescriptor.VERSION) { throw new VerifyCodeSignException("Invalid fs-verify descriptor version of ElfSignBlock"); } byte inFsHashAlgorithm = bf.get(); @@ -198,8 +202,9 @@ public class FsVerityDescriptor { writeBytesWithSize(buffer, rawRootHash, ROOT_HASH_FILED_SIZE); writeBytesWithSize(buffer, salt, SALT_SIZE); buffer.putInt(flags); - writeBytesWithSize(buffer, null, RESERVED_SIZE_AFTER_FLAGS); + buffer.putInt(0); buffer.putLong(merkleTreeOffset); + buffer.putLong(0); writeBytesWithSize(buffer, null, RESERVED_SIZE_AFTER_TREE_OFFSET); buffer.put(csVersion); return buffer.array(); @@ -211,7 +216,7 @@ public class FsVerityDescriptor { * @return bytes of descriptor * @throws FsVerityDigestException if error */ - public byte[] getByteForGenerateDigest() throws FsVerityDigestException { + public byte[] getDiscByte() throws FsVerityDigestException { ByteBuffer buffer = ByteBuffer.allocate(DESCRIPTOR_SIZE).order(ByteOrder.LITTLE_ENDIAN); buffer.put(CODE_SIGN_VERSION); buffer.put(hashAlgorithm); @@ -225,8 +230,39 @@ public class FsVerityDescriptor { writeBytesWithSize(buffer, rawRootHash, ROOT_HASH_FILED_SIZE); writeBytesWithSize(buffer, salt, SALT_SIZE); buffer.putInt(flags); - writeBytesWithSize(buffer, null, RESERVED_SIZE_AFTER_FLAGS); + buffer.putInt(0); + buffer.putLong(merkleTreeOffset); + return buffer.array(); + } + + /** + * Get bytes for generate digest, cs_version 2 + * + * @param mapOffset bit map data offset at file + * @param mapSize bit map size + * @param unitSize bit map unit size corresponding to each page + * @return bytes of descriptor + * @throws FsVerityDigestException if error + */ + public byte[] getDiscByteCsv2(long mapOffset, long mapSize, byte unitSize) throws FsVerityDigestException { + ByteBuffer buffer = ByteBuffer.allocate(DESCRIPTOR_SIZE).order(ByteOrder.LITTLE_ENDIAN); + buffer.put(VERSION); + buffer.put(hashAlgorithm); + buffer.put(log2BlockSize); + if (this.saltSize > SALT_SIZE) { + throw new FsVerityDigestException("Salt is too long"); + } + buffer.put(this.saltSize); + buffer.putInt(0); + buffer.putLong(fileSize); + writeBytesWithSize(buffer, rawRootHash, ROOT_HASH_FILED_SIZE); + writeBytesWithSize(buffer, salt, SALT_SIZE); + buffer.putInt((unitSize << 1 | flags)); + buffer.putInt((int) mapSize); buffer.putLong(merkleTreeOffset); + buffer.putLong(mapOffset); + writeBytesWithSize(buffer, null, RESERVED_SIZE_AFTER_TREE_OFFSET); + buffer.put(CODE_SIGN_VERSION_V2); return buffer.array(); } @@ -271,8 +307,12 @@ public class FsVerityDescriptor { private int flags; + private int bitMapSize; + private long merkleTreeOffset; + private long bitMapOffset; + private byte csVersion; public Builder setVersion(byte version) { diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/fsverity/FsVerityGenerator.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/fsverity/FsVerityGenerator.java index 57501d09d6ab5e056e991a4fcd669340b362fa94..67c0a9e8f8500231110d9c10ea27e0e19d214f3e 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/fsverity/FsVerityGenerator.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/fsverity/FsVerityGenerator.java @@ -15,6 +15,7 @@ package com.ohos.hapsigntool.codesigning.fsverity; +import com.ohos.hapsigntool.codesigning.datastructure.PageInfoExtension; import com.ohos.hapsigntool.codesigning.exception.FsVerityDigestException; import com.ohos.hapsigntool.codesigning.utils.DigestUtils; @@ -42,10 +43,29 @@ public class FsVerityGenerator { private byte[] fsVerityDigest = null; + private byte[] fsVerityDigestV2 = null; + private byte[] treeBytes = null; private byte[] rootHash = null; + private PageInfoExtension pageInfoExtension; + + /** + * Constructor for FsVerityGenerator + */ + public FsVerityGenerator() { + } + + /** + * Constructor for FsVerityGenerator + * + * @param pg PageInfoExtension + */ + public FsVerityGenerator(PageInfoExtension pg) { + this.pageInfoExtension = pg; + } + /** * generate merkle tree of given input * @@ -94,14 +114,25 @@ public class FsVerityGenerator { .setRawRootHash(merkleTree.rootHash) .setFlags(flags) .setMerkleTreeOffset(fsvTreeOffset); - byte[] fsVerityDescriptor = builder.build().getByteForGenerateDigest(); - byte[] digest; try { - digest = DigestUtils.computeDigest(fsVerityDescriptor, FS_VERITY_HASH_ALGORITHM.getHashAlgorithm()); + byte[] fsVerityDescriptor = builder.build().getDiscByte(); + byte[] digest = DigestUtils.computeDigest(fsVerityDescriptor, FS_VERITY_HASH_ALGORITHM.getHashAlgorithm()); + fsVerityDigest = FsVerityDigest.getFsVerityDigest(FS_VERITY_HASH_ALGORITHM.getId(), digest); } catch (NoSuchAlgorithmException e) { throw new FsVerityDigestException("Invalid algorithm" + e.getMessage(), e); } - fsVerityDigest = FsVerityDigest.getFsVerityDigest(FS_VERITY_HASH_ALGORITHM.getId(), digest); + if (pageInfoExtension != null && flags != 0) { + try { + byte[] fsVerityDescriptorV2 = builder.build() + .getDiscByteCsv2(pageInfoExtension.getMapOffset(), pageInfoExtension.getMapSize(), + pageInfoExtension.getUnitSize()); + byte[] digest = DigestUtils.computeDigest(fsVerityDescriptorV2, + FS_VERITY_HASH_ALGORITHM.getHashAlgorithm()); + fsVerityDigestV2 = FsVerityDigest.getFsVerityDigest(FS_VERITY_HASH_ALGORITHM.getId(), digest); + } catch (NoSuchAlgorithmException e) { + throw new FsVerityDigestException("Invalid algorithm" + e.getMessage(), e); + } + } treeBytes = merkleTree.tree; rootHash = merkleTree.rootHash; } @@ -115,6 +146,15 @@ public class FsVerityGenerator { return fsVerityDigest; } + /** + * Get FsVerity digest + * + * @return bytes of FsVerity digest + */ + public byte[] getFsVerityDigestV2() { + return fsVerityDigestV2; + } + /** * Get merkle tree in bytes * diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/CodeSigning.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/CodeSigning.java index 7993a7fcddf9293c2f6f203e881b054bc12b7243..3792e356727a2bd183ae8cd9b2be5ed2c6cac47b 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/CodeSigning.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/CodeSigning.java @@ -20,6 +20,7 @@ import com.ohos.hapsigntool.codesigning.datastructure.ElfSignBlock; import com.ohos.hapsigntool.codesigning.datastructure.Extension; import com.ohos.hapsigntool.codesigning.datastructure.FsVerityInfoSegment; import com.ohos.hapsigntool.codesigning.datastructure.MerkleTreeExtension; +import com.ohos.hapsigntool.codesigning.datastructure.PageInfoExtension; import com.ohos.hapsigntool.codesigning.datastructure.SignInfo; import com.ohos.hapsigntool.codesigning.exception.CodeSignException; import com.ohos.hapsigntool.codesigning.exception.FsVerityDigestException; @@ -34,6 +35,7 @@ import com.ohos.hapsigntool.error.ProfileException; import com.ohos.hapsigntool.signer.LocalSigner; import com.ohos.hapsigntool.utils.FileUtils; import com.ohos.hapsigntool.utils.StringUtils; +import com.ohos.hapsigntool.zip.EntryType; import com.ohos.hapsigntool.zip.Zip; import com.ohos.hapsigntool.zip.ZipEntry; import com.ohos.hapsigntool.zip.ZipEntryHeader; @@ -78,14 +80,12 @@ public class CodeSigning { private static final Logger LOGGER = LogManager.getLogger(CodeSigning.class); - private static final String NATIVE_LIB_AN_SUFFIX = ".an"; - - private static final String NATIVE_LIB_SO_SUFFIX = ".so"; - private final SignerConfig signConfig; private CodeSignBlock codeSignBlock; + private PageInfoExtension pageInfoExtension; + /** * provide code sign functions to sign a hap * @@ -180,7 +180,7 @@ public class CodeSigning { LOGGER.debug("Sign hap."); String ownerID = HapUtils.getAppIdentifier(profileContent); - + createPageInfoExtension(zip); try (FileInputStream inputStream = new FileInputStream(input)) { Pair hapSignInfoAndMerkleTreeBytesPair = signFile(inputStream, dataSize, true, fsvTreeOffset, ownerID); @@ -206,12 +206,19 @@ public class CodeSigning { return generated; } + private void createPageInfoExtension(Zip zip) { + long[] bitmapOffSize = PageInfoGenerator.getPageInfoFromZip(zip); + if (bitmapOffSize.length == 2) { + pageInfoExtension = new PageInfoExtension(bitmapOffSize[0], bitmapOffSize[1]); + } + } + private long computeDataSize(Zip zip) throws HapFormatException { long dataSize = 0L; for (ZipEntry entry : zip.getZipEntries()) { ZipEntryHeader zipEntryHeader = entry.getZipEntryData().getZipEntryHeader(); - if (FileUtils.isRunnableFile(zipEntryHeader.getFileName()) - && zipEntryHeader.getMethod() == Zip.FILE_UNCOMPRESS_METHOD_FLAG) { + EntryType type = entry.getZipEntryData().getType(); + if (EntryType.BitMap.equals(type) || EntryType.RunnableFile.equals(type)) { continue; } // if the first file is not uncompressed abc or so, set dataSize to zero @@ -344,7 +351,7 @@ public class CodeSigning { if (StringUtils.isEmpty(entryName)) { return false; } - if (entryName.endsWith(NATIVE_LIB_AN_SUFFIX)) { + if (entryName.endsWith(FileUtils.NATIVE_LIB_AN_SUFFIX)) { return true; } if (entryName.startsWith(FileUtils.LIBS_PATH_PREFIX)) { @@ -403,7 +410,7 @@ public class CodeSigning { */ public Pair signFile(InputStream inputStream, long fileSize, boolean storeTree, long fsvTreeOffset, String ownerID) throws FsVerityDigestException, CodeSignException { - FsVerityGenerator fsVerityGenerator = new FsVerityGenerator(); + FsVerityGenerator fsVerityGenerator = new FsVerityGenerator(pageInfoExtension); fsVerityGenerator.generateFsVerityDigest(inputStream, fileSize, fsvTreeOffset); byte[] fsVerityDigest = fsVerityGenerator.getFsVerityDigest(); byte[] signature = generateSignature(fsVerityDigest, ownerID); @@ -419,6 +426,12 @@ public class CodeSigning { Extension merkleTreeExtension = new MerkleTreeExtension(merkleTreeSize, fsvTreeOffset, fsVerityGenerator.getRootHash()); signInfo.addExtension(merkleTreeExtension); + if (pageInfoExtension != null) { + byte[] fsVerityDigestV2 = fsVerityGenerator.getFsVerityDigestV2(); + byte[] signatureV2 = generateSignature(fsVerityDigestV2, ownerID); + pageInfoExtension.setSignature(signatureV2); + signInfo.addExtension(pageInfoExtension); + } } return Pair.create(signInfo, fsVerityGenerator.getTreeBytes()); } diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/PageInfoGenerator.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/PageInfoGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..85214c96db2396b348ea6a8b3bfe267beda0b8a9 --- /dev/null +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/PageInfoGenerator.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * 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.ohos.hapsigntool.codesigning.sign; + +import com.ohos.hapsigntool.codesigning.datastructure.PageInfoExtension; +import com.ohos.hapsigntool.codesigning.elf.ElfFile; +import com.ohos.hapsigntool.codesigning.elf.ElfProgramHeader; +import com.ohos.hapsigntool.codesigning.exception.ElfFormatException; +import com.ohos.hapsigntool.error.HapFormatException; +import com.ohos.hapsigntool.utils.FileUtils; +import com.ohos.hapsigntool.zip.EntryType; +import com.ohos.hapsigntool.zip.Zip; +import com.ohos.hapsigntool.zip.ZipEntry; +import com.ohos.hapsigntool.zip.ZipEntryHeader; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * pages info bitmap generator + * + * @since 2024/07/01 + */ +public class PageInfoGenerator { + private static final byte ABC_M_CODE = 2; + + private static final byte ELF_M_CODE = 1; + + private static final long PAGE_SIZE_4K = 4096L; + + private static final long PAGE_SIZE_1K = PAGE_SIZE_4K / PageInfoExtension.DEFAULT_UNIT_SIZE; + + private static final Logger LOGGER = LogManager.getLogger(PageInfoGenerator.class); + + private long maxEntryDataOffset = 0L; + + private final List excSegmentList = new ArrayList<>(); + + /** + * Constructor for PageInfoGenerator + * + * @param input file + * @throws IOException io error + * @throws ElfFormatException elf file format error + */ + public PageInfoGenerator(File input) throws IOException, ElfFormatException, HapFormatException { + new PageInfoGenerator(new Zip(input)); + } + + /** + * Constructor for PageInfoGenerator + * + * @param zip zip + * @throws IOException io error + * @throws ElfFormatException elf file format error + */ + public PageInfoGenerator(Zip zip) throws IOException, ElfFormatException, HapFormatException { + Map runnableFileNames = new LinkedHashMap<>(); + List zipEntries = zip.getZipEntries(); + for (ZipEntry entry : zipEntries) { + ZipEntryHeader zipEntryHeader = entry.getZipEntryData().getZipEntryHeader(); + long entryDataOffset = entry.getCentralDirectory().getOffset() + ZipEntryHeader.HEADER_LENGTH + + zipEntryHeader.getFileNameLength() + zipEntryHeader.getExtraLength(); + if (entryDataOffset % PAGE_SIZE_4K != 0) { + throw new HapFormatException( + String.format(Locale.ROOT, "Invalid entryDataOffset(%d), not a multiple of 4096", maxEntryDataOffset)); + } + if (EntryType.RunnableFile.equals(entry.getZipEntryData().getType())) { + runnableFileNames.put(zipEntryHeader.getFileName(), entryDataOffset); + continue; + } + maxEntryDataOffset = entryDataOffset; + break; + } + File input = new File(zip.getFile()); + try (JarFile hap = new JarFile(input, false)) { + for (Map.Entry en : runnableFileNames.entrySet()) { + this.libExecSegment(hap, en.getKey(), en.getValue()); + } + } + } + + private void libExecSegment(JarFile hap, String libFileName, long entryDataOffset) + throws IOException, ElfFormatException { + JarEntry libEntry = hap.getJarEntry(libFileName); + if (libFileName.endsWith(FileUtils.ABC_FILE_SUFFIX)) { + long size = libEntry.getSize(); + excSegmentList.add(new ExcSegment(ABC_M_CODE, libFileName, entryDataOffset, entryDataOffset + size)); + } else { + try (InputStream stream = hap.getInputStream(libEntry)) { + ElfFile elfFile = new ElfFile(stream); + List elfPHeaders = elfFile.filterExecPHeaders(); + for (ElfProgramHeader programHeader : elfPHeaders) { + long pOffset = programHeader.getPOffset(); + long pFilesz = programHeader.getPFilesz(); + long off = entryDataOffset + pOffset; + long endoff = off + pFilesz; + excSegmentList.add(new ExcSegment(ELF_M_CODE, libFileName, off, endoff)); + } + } catch (ElfFormatException e) { + throw new ElfFormatException(libFileName + " error : " + e.getMessage(), e); + } + } + } + + /** + * generate bitMap + * + * @return byte array of bitmap + * @throws HapFormatException hap format error + */ + public byte[] generateBitMap() throws HapFormatException { + if (maxEntryDataOffset % PAGE_SIZE_4K != 0) { + throw new HapFormatException( + String.format(Locale.ROOT, "Invalid maxEndOff(%d), not a multiple of 4096", maxEntryDataOffset)); + } + int len = (int) (maxEntryDataOffset / PAGE_SIZE_1K); + BitSet bitmap = new BitSet(len); + for (ExcSegment es : excSegmentList) { + int begin = (int) (es.getStartOffset() >> 12) * PageInfoExtension.DEFAULT_UNIT_SIZE; + int end = (es.getEndOffset() % PAGE_SIZE_4K == 0) + ? (int) ((es.getEndOffset() >> 12)) * PageInfoExtension.DEFAULT_UNIT_SIZE + : (int) ((es.getEndOffset() >> 12) + 1) * PageInfoExtension.DEFAULT_UNIT_SIZE; + for (int i = begin; i < end; i = i + 4) { + if ((ELF_M_CODE == es.getType())) { + bitmap.set(i + 3); + } else { + bitmap.set(i + 2); + } + } + } + long[] longArray = bitmap.toLongArray(); + ByteBuffer buffer = ByteBuffer.allocate(longArray.length * 8); + for (long l : longArray) { + buffer.putLong(Long.reverse(l)); + } + return buffer.array(); + } + + /** + * get bitmap file's offset and size + * + * @param zip hap file + * @return long array of offset and size + */ + public static long[] getPageInfoFromZip(Zip zip) { + List zipEntries = zip.getZipEntries(); + for (ZipEntry e : zipEntries) { + String fileName = e.getCentralDirectory().getFileName(); + if (!FileUtils.BIT_MAP_FILENAME.equals(fileName)) { + continue; + } + long fileOffset = e.getCentralDirectory().getOffset() + ZipEntryHeader.HEADER_LENGTH + e.getZipEntryData() + .getZipEntryHeader() + .getFileNameLength() + e.getZipEntryData().getZipEntryHeader().getExtraLength(); + long fileSize = e.getCentralDirectory().getUnCompressedSize(); + long bitmapSize = fileSize * 8; + return new long[] {fileOffset, bitmapSize}; + } + return new long[0]; + } + + static class ExcSegment { + /** + * abc or elf + */ + private byte type; + + private String fileName; + + private long startOffset; + + private long endOffset; + + ExcSegment(byte type, String fileName, long startOffset, long endOffset) { + this.type = type; + this.fileName = fileName; + this.startOffset = startOffset; + this.endOffset = endOffset; + } + + public byte getType() { + return type; + } + + public String getFileName() { + return fileName; + } + + public long getStartOffset() { + return startOffset; + } + + public long getEndOffset() { + return endOffset; + } + } +} diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/provider/SignProvider.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/provider/SignProvider.java index edf3bf6e1eb1abd6c509aa9d5266dd14fa8a0870..8433fa371bf5cec76795ce37707ef752ee6ce92a 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/provider/SignProvider.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/provider/SignProvider.java @@ -19,6 +19,8 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonParser; +import com.ohos.hapsigntool.codesigning.exception.ElfFormatException; +import com.ohos.hapsigntool.codesigning.sign.PageInfoGenerator; import com.ohos.hapsigntool.entity.Options; import com.ohos.hapsigntool.codesigning.exception.CodeSignException; import com.ohos.hapsigntool.codesigning.exception.FsVerityDigestException; @@ -336,7 +338,7 @@ public abstract class SignProvider { } // copy file and Alignment int alignment = Integer.parseInt(signParams.get(ParamConstants.PARAM_BASIC_ALIGNMENT)); - Zip zip = copyFileAndAlignment(input, tmpOutput, alignment); + Zip zip = copyFileAndAlignment(input, tmpOutput, alignment, suffix); // generate sign block and output signedHap try (RandomAccessFile outputHap = new RandomAccessFile(tmpOutput, "rw")) { ZipDataInput outputHapIn = new RandomAccessFileZipDataInput(outputHap); @@ -363,8 +365,9 @@ public abstract class SignProvider { outputSignedFile(outputHap, centralDirectoryOffset, signingBlock, centralDirectory, eocdBuffer); isRet = true; } - } catch (FsVerityDigestException | InvalidKeyException | HapFormatException | MissingParamsException -|InvalidParamsException |ProfileException |NumberFormatException |CustomException |IOException |CodeSignException e) { + } catch (FsVerityDigestException | InvalidKeyException | HapFormatException | MissingParamsException | + InvalidParamsException | ProfileException | NumberFormatException | CustomException | IOException | + CodeSignException | ElfFormatException e) { printErrorLogWithoutStack(e); } catch (SignatureException e) { printErrorLog(e); @@ -500,11 +503,20 @@ public abstract class SignProvider { * @return zip zip * @throws IOException io error * @throws HapFormatException hap format error + * */ - private Zip copyFileAndAlignment(File input, File tmpOutput, int alignment) - throws IOException, HapFormatException { + private Zip copyFileAndAlignment(File input, File tmpOutput, int alignment, String suffix) + throws IOException, HapFormatException, ElfFormatException { Zip zip = new Zip(input); zip.alignment(alignment); + if ("hap".equals(suffix)) { + PageInfoGenerator pageInfoGenerator = new PageInfoGenerator(zip); + byte[] bitMap = pageInfoGenerator.generateBitMap(); + if (bitMap != null) { + zip.addBitMap(bitMap); + zip.alignment(alignment); + } + } zip.removeSignBlock(); long start = System.currentTimeMillis(); zip.toFile(tmpOutput.getCanonicalPath()); diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/utils/FileUtils.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/utils/FileUtils.java index 72301e87a4bfb319ed94fb37b552b06bf4141870..fe65899ab981ac00da3c5120ce01c8d906ec10a0 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/utils/FileUtils.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/utils/FileUtils.java @@ -78,6 +78,21 @@ public final class FileUtils { */ public static final String LIBS_PATH_PREFIX = "libs/"; + /** + * abc file suffix + */ + public static final String ABC_FILE_SUFFIX = ".abc"; + + /** + * an file suffix + */ + public static final String NATIVE_LIB_AN_SUFFIX = ".an"; + + /** + * bitmap file name + */ + public static final String BIT_MAP_FILENAME = ".pages.info"; + private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; private FileUtils() { @@ -494,7 +509,7 @@ public final class FileUtils { if (StringUtils.isEmpty(name)) { return false; } - if (name.endsWith(".an") || name.endsWith(".abc")) { + if (name.endsWith(NATIVE_LIB_AN_SUFFIX) || name.endsWith(ABC_FILE_SUFFIX)) { return true; } if (name.startsWith(LIBS_PATH_PREFIX)) { diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/CentralDirectory.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/CentralDirectory.java index 04adc9b4260579ab487baa6b3a972068313d03bd..37e8d1d02c8258fbbd222b7e51c735b45aec0c3f 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/CentralDirectory.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/CentralDirectory.java @@ -155,6 +155,13 @@ public class CentralDirectory { private int length; + /** + * updateLength + */ + public void updateLength() { + length = CD_LENGTH + fileNameLength + extraLength + commentLength; + } + /** * get Central Directory * @@ -199,7 +206,7 @@ public class CentralDirectory { bf.get(readComment); cd.setComment(readComment); } - cd.setLength(CD_LENGTH + cd.getFileNameLength() + cd.getExtraLength() + cd.getCommentLength()); + cd.updateLength(); return cd; } diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/EndOfCentralDirectory.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/EndOfCentralDirectory.java index 92d211b305612c48bfd453e3478ab2ab3d7be970..6936dadfd59cdf7a9f51125baa7b89ec9f97c56d 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/EndOfCentralDirectory.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/EndOfCentralDirectory.java @@ -123,8 +123,8 @@ public class EndOfCentralDirectory { eocd.setDiskNum(UnsignedDecimalUtil.getUnsignedShort(bf)); eocd.setcDStartDiskNum(UnsignedDecimalUtil.getUnsignedShort(bf)); eocd.setThisDiskCDNum(UnsignedDecimalUtil.getUnsignedShort(bf)); - eocd.setcDTotal(UnsignedDecimalUtil.getUnsignedShort(bf)); - eocd.setcDSize(UnsignedDecimalUtil.getUnsignedInt(bf)); + eocd.setCDTotal(UnsignedDecimalUtil.getUnsignedShort(bf)); + eocd.setCDSize(UnsignedDecimalUtil.getUnsignedInt(bf)); eocd.setOffset(UnsignedDecimalUtil.getUnsignedInt(bf)); eocd.setCommentLength(UnsignedDecimalUtil.getUnsignedShort(bf)); if (bf.remaining() != eocd.getCommentLength()) { @@ -195,19 +195,19 @@ public class EndOfCentralDirectory { this.thisDiskCDNum = thisDiskCDNum; } - public int getcDTotal() { + public int getCDTotal() { return cDTotal; } - public void setcDTotal(int cDTotal) { + public void setCDTotal(int cDTotal) { this.cDTotal = cDTotal; } - public long getcDSize() { + public long getCDSize() { return cDSize; } - public void setcDSize(long cDSize) { + public void setCDSize(long cDSize) { this.cDSize = cDSize; } diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/EntryType.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/EntryType.java new file mode 100644 index 0000000000000000000000000000000000000000..46d44b136242b291b6fcbb3d9195558dc454a531 --- /dev/null +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/EntryType.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024-2024 Huawei Device Co., Ltd. + * 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.ohos.hapsigntool.zip; + +/** + * Entry Type + * + * @since 2024/06/25 + */ +public enum EntryType { + RunnableFile, + BitMap, + ResourceFile; +} diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/Zip.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/Zip.java index 7ab9bdc2ed2b9a31894b6837620bd6b1d93411f9..55cfe9526e11ed0ca573edf7a38eb31fc3b8d92f 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/Zip.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/Zip.java @@ -19,7 +19,6 @@ import com.ohos.hapsigntool.error.CustomException; import com.ohos.hapsigntool.error.ERROR; import com.ohos.hapsigntool.error.ZipException; import com.ohos.hapsigntool.utils.FileUtils; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -43,7 +42,7 @@ public class Zip { /** * file is uncompress file flag */ - public static final int FILE_UNCOMPRESS_METHOD_FLAG = 0; + public static final short FILE_UNCOMPRESS_METHOD_FLAG = 0; /** * max comment length @@ -129,9 +128,9 @@ public class Zip { } private void getZipCentralDirectory(File file) throws IOException { - zipEntries = new ArrayList<>(endOfCentralDirectory.getcDTotal()); + zipEntries = new ArrayList<>(endOfCentralDirectory.getCDTotal()); // read full central directory bytes - byte[] cdBytes = FileUtils.readFileByOffsetAndLength(file, cDOffset, endOfCentralDirectory.getcDSize()); + byte[] cdBytes = FileUtils.readFileByOffsetAndLength(file, cDOffset, endOfCentralDirectory.getCDSize()); if (cdBytes.length < CentralDirectory.CD_LENGTH) { throw new ZipException("find zip cd failed"); } @@ -189,8 +188,15 @@ public class Zip { for (ZipEntry entry : zipEntries) { ZipEntryData zipEntryData = entry.getZipEntryData(); FileUtils.writeByteToOutFile(zipEntryData.getZipEntryHeader().toBytes(), fos); - boolean isSuccess = FileUtils.appendWriteFileByOffsetToFile(file, fos, - zipEntryData.getFileOffset(), zipEntryData.getFileSize()); + boolean isSuccess; + if (entry.getZipEntryData().getType() == EntryType.BitMap) { + ByteBuffer bf = ByteBuffer.wrap(entry.getZipEntryData().getData()); + bf.order(ByteOrder.LITTLE_ENDIAN); + isSuccess = FileUtils.writeByteToOutFile(bf.array(), fos); + } else { + isSuccess = FileUtils.appendWriteFileByOffsetToFile(file, fos, + zipEntryData.getFileOffset(), zipEntryData.getFileSize()); + } if (!isSuccess) { throw new ZipException("write zip data failed"); } @@ -228,8 +234,8 @@ public class Zip { break; } int alignBytes; - if (method == FILE_UNCOMPRESS_METHOD_FLAG && FileUtils.isRunnableFile( - zipEntryData.getZipEntryHeader().getFileName())) { + EntryType type = entry.getZipEntryData().getType(); + if (type == EntryType.RunnableFile || type == EntryType.BitMap) { // .abc and .so file align 4096 byte. alignBytes = 4096; } else if (isFirstUnRunnableFile) { @@ -250,6 +256,24 @@ public class Zip { } } + public void addBitMap(byte[] data) throws ZipException { + for (ZipEntry e : zipEntries) { + if (e.getZipEntryData().getType() == EntryType.BitMap) { + e.getZipEntryData().setData(data); + e.getZipEntryData().getZipEntryHeader().setUnCompressedSize(data.length); + e.getZipEntryData().getZipEntryHeader().setCompressedSize(data.length); + return; + } + } + ZipEntry entry = new ZipEntry.Builder().setMethod(FILE_UNCOMPRESS_METHOD_FLAG) + .setUncompressedSize(data.length) + .setCompressedSize(data.length) + .setFileName(FileUtils.BIT_MAP_FILENAME) + .setData(data) + .build(); + zipEntries.add(entry); + } + /** * remove sign block */ @@ -262,22 +286,19 @@ public class Zip { * sort uncompress entry in the front. */ private void sort() { - // sort uncompress file (so, abc, an) - other uncompress file - compress file + // sort uncompress file (so, abc, an) - bitmap - other uncompress file - compress file zipEntries.sort((entry1, entry2) -> { short entry1Method = entry1.getZipEntryData().getZipEntryHeader().getMethod(); short entry2Method = entry2.getZipEntryData().getZipEntryHeader().getMethod(); String entry1FileName = entry1.getZipEntryData().getZipEntryHeader().getFileName(); String entry2FileName = entry2.getZipEntryData().getZipEntryHeader().getFileName(); if (entry1Method == FILE_UNCOMPRESS_METHOD_FLAG && entry2Method == FILE_UNCOMPRESS_METHOD_FLAG) { - boolean isRunnableFile1 = FileUtils.isRunnableFile(entry1FileName); - boolean isRunnableFile2 = FileUtils.isRunnableFile(entry2FileName); - if (isRunnableFile1 && isRunnableFile2) { - return entry1FileName.compareTo(entry2FileName); - } else if (isRunnableFile1) { - return -1; - } else if (isRunnableFile2) { - return 1; + EntryType entry1Type = entry1.getZipEntryData().getType(); + EntryType entry2Type = entry2.getZipEntryData().getType(); + if (entry1Type != entry2Type) { + return entry1Type.compareTo(entry2Type); } + return entry1FileName.compareTo(entry2FileName); } else if (entry1Method == FILE_UNCOMPRESS_METHOD_FLAG) { return -1; } else if (entry2Method == FILE_UNCOMPRESS_METHOD_FLAG) { @@ -292,6 +313,7 @@ public class Zip { long offset = 0L; long cdLength = 0L; for (ZipEntry entry : zipEntries) { + entry.updateLength(); entry.getCentralDirectory().setOffset(offset); offset += entry.getZipEntryData().getLength(); cdLength += entry.getCentralDirectory().getLength(); @@ -301,9 +323,11 @@ public class Zip { } cDOffset = offset; endOfCentralDirectory.setOffset(offset); - endOfCentralDirectory.setcDSize(cdLength); + endOfCentralDirectory.setCDSize(cdLength); offset += cdLength; eOCDOffset = offset; + endOfCentralDirectory.setCDTotal(zipEntries.size()); + endOfCentralDirectory.setThisDiskCDNum(zipEntries.size()); } public List getZipEntries() { diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/ZipEntry.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/ZipEntry.java index ff66339ad690b1ddacba48b9ec301cb80c16dc5e..03682ddd6fc3bc81222ac77cf60e9c5697d523fa 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/ZipEntry.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/ZipEntry.java @@ -18,6 +18,7 @@ package com.ohos.hapsigntool.zip; import com.ohos.hapsigntool.error.ZipException; import java.util.Arrays; +import java.util.zip.CRC32; /** * ZipEntry and CentralDirectory data @@ -27,7 +28,15 @@ import java.util.Arrays; public class ZipEntry { private ZipEntryData zipEntryData; - private CentralDirectory fileEntryIncentralDirectory; + private CentralDirectory fileEntryInCentralDirectory; + + /** + * updateLength + */ + public void updateLength() { + zipEntryData.updateLength(); + fileEntryInCentralDirectory.updateLength(); + } /** * alignment one entry @@ -40,7 +49,7 @@ public class ZipEntry { // if cd extra len bigger than entry extra len, make cd and entry extra length equals int padding = calZeroPaddingLengthForEntryExtra(); int remainder = (int) ((zipEntryData.getZipEntryHeader().getLength() - + fileEntryIncentralDirectory.getOffset()) % alignNum); + + fileEntryInCentralDirectory.getOffset()) % alignNum); if (remainder == 0) { return padding; @@ -58,7 +67,7 @@ public class ZipEntry { private int calZeroPaddingLengthForEntryExtra() throws ZipException { int entryExtraLen = zipEntryData.getZipEntryHeader().getExtraLength(); - int cdExtraLen = fileEntryIncentralDirectory.getExtraLength(); + int cdExtraLen = fileEntryInCentralDirectory.getExtraLength(); if (cdExtraLen > entryExtraLen) { setEntryHeaderNewExtraLength(cdExtraLen); return cdExtraLen - entryExtraLen; @@ -71,12 +80,12 @@ public class ZipEntry { } private void setCenterDirectoryNewExtraLength(int newLength) throws ZipException { - byte[] newCDExtra = getAlignmentNewExtra(newLength, fileEntryIncentralDirectory.getExtraData()); - fileEntryIncentralDirectory.setExtraData(newCDExtra); - fileEntryIncentralDirectory.setExtraLength(newLength); - fileEntryIncentralDirectory.setLength(CentralDirectory.CD_LENGTH - + fileEntryIncentralDirectory.getFileNameLength() - + fileEntryIncentralDirectory.getExtraLength() + fileEntryIncentralDirectory.getCommentLength()); + byte[] newCDExtra = getAlignmentNewExtra(newLength, fileEntryInCentralDirectory.getExtraData()); + fileEntryInCentralDirectory.setExtraData(newCDExtra); + fileEntryInCentralDirectory.setExtraLength(newLength); + fileEntryInCentralDirectory.setLength(CentralDirectory.CD_LENGTH + + fileEntryInCentralDirectory.getFileNameLength() + + fileEntryInCentralDirectory.getExtraLength() + fileEntryInCentralDirectory.getCommentLength()); } private void setEntryHeaderNewExtraLength(int newLength) throws ZipException { @@ -109,10 +118,144 @@ public class ZipEntry { } public CentralDirectory getCentralDirectory() { - return fileEntryIncentralDirectory; + return fileEntryInCentralDirectory; } public void setCentralDirectory(CentralDirectory centralDirectory) { - this.fileEntryIncentralDirectory = centralDirectory; + this.fileEntryInCentralDirectory = centralDirectory; + } + + public static class Builder { + private short version = 10; + + private short flag = 2048; + + private short method = 0; + + private long compressedSize; + + private long unCompressedSize; + + private String fileName; + + private byte[] extraData; + + private byte[] comment; + + private byte[] data; + + public Builder setVersion(short version) { + this.version = version; + return this; + } + + public Builder setFlag(short flag) { + this.flag = flag; + return this; + } + + public Builder setMethod(short method) { + this.method = method; + return this; + } + + public Builder setCompressedSize(long compressedSize) { + this.compressedSize = compressedSize; + return this; + } + + public Builder setUncompressedSize(long unCompressedSize) { + this.unCompressedSize = unCompressedSize; + return this; + } + + public Builder setFileName(String fileName) { + this.fileName = fileName; + return this; + } + + public Builder setExtraData(byte[] extraData) { + this.extraData = extraData; + return this; + } + + public Builder setComment(byte[] comment) { + this.comment = comment; + return this; + } + + public Builder setData(byte[] data) { + this.data = data; + return this; + } + + public ZipEntry build() throws ZipException { + ZipEntry entry = new ZipEntry(); + ZipEntryData zipEntryData = new ZipEntryData(); + zipEntryData.setData(data); + + ZipEntryHeader zipEntryHeader = new ZipEntryHeader(); + CentralDirectory cd = new CentralDirectory(); + + cd.setVersion(version); + cd.setVersionExtra(version); + zipEntryHeader.setVersion(version); + cd.setFlag(flag); + zipEntryHeader.setFlag(flag); + cd.setMethod(method); + zipEntryHeader.setMethod(method); + + long time = System.currentTimeMillis(); + cd.setLastTime((short) (time >> 32)); + cd.setLastDate((short) time); + zipEntryHeader.setLastTime((short) (time >> 32)); + zipEntryHeader.setLastDate((short) time); + + cd.setCompressedSize(compressedSize); + zipEntryHeader.setCompressedSize(compressedSize); + cd.setUnCompressedSize(unCompressedSize); + zipEntryHeader.setUnCompressedSize(unCompressedSize); + + cd.setFileName(fileName); + cd.setFileNameLength(fileName.length()); + zipEntryHeader.setFileName(fileName); + zipEntryHeader.setFileNameLength(fileName.length()); + + if (extraData != null) { + cd.setExtraData(extraData); + cd.setExtraLength(extraData.length); + zipEntryHeader.setExtraData(extraData); + zipEntryHeader.setExtraLength(extraData.length); + } else { + cd.setExtraLength(0); + zipEntryHeader.setExtraLength(0); + } + if (comment != null) { + cd.setComment(comment); + cd.setCommentLength(comment.length); + } else { + cd.setCommentLength(0); + } + cd.setDiskNumStart(0); + cd.setExternalFile(0); + + cd.updateLength(); + zipEntryHeader.updateLength(); + + if (data == null) { + throw new ZipException("can not find entry data"); + } + final CRC32 c = new CRC32(); + c.update(data); + final int crc32 = new Long(c.getValue()).intValue(); + cd.setCrc32(crc32); + zipEntryHeader.setCrc32(crc32); + + zipEntryData.setZipEntryHeader(zipEntryHeader); + entry.setZipEntryData(zipEntryData); + zipEntryData.setType(EntryType.BitMap); + entry.setCentralDirectory(cd); + return entry; + } } } \ No newline at end of file diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/ZipEntryData.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/ZipEntryData.java index 35b19e7ad5833b31343f479a92d321a808507651..fc9327cb65ce0ac3b0192918ae21be2de48782af 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/ZipEntryData.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/ZipEntryData.java @@ -51,6 +51,22 @@ public class ZipEntryData { return zipEntryHeader; } + private EntryType type; + + private byte[] data; + + /** + * updateLength + */ + public void updateLength() { + zipEntryHeader.updateLength(); + if (type == EntryType.BitMap) { + length = zipEntryHeader.getLength() + data.length + (dataDescriptor == null ? 0 : 16); + } else { + length = zipEntryHeader.getLength() + fileSize + (dataDescriptor == null ? 0 : 16); + } + } + /** * init zip entry by file * @@ -86,7 +102,17 @@ public class ZipEntryData { ZipEntryData entry = new ZipEntryData(); entry.setFileOffset(offset); entry.setFileSize(fileSize); - input.skip(fileSize); + byte[] data = FileUtils.readInputByLength(input, fileSize); + + if (entryHeader.getMethod() == Zip.FILE_UNCOMPRESS_METHOD_FLAG + && FileUtils.isRunnableFile(entryHeader.getFileName())) { + entry.setType(EntryType.RunnableFile); + } else if (entryHeader.getFileName().equals(FileUtils.BIT_MAP_FILENAME)) { + entry.setType(EntryType.BitMap); + entry.data = data; + } else { + entry.setType(EntryType.ResourceFile); + } long entryLength = entryHeader.getLength() + fileSize; short flag = entryHeader.getFlag(); @@ -140,4 +166,20 @@ public class ZipEntryData { public void setLength(long length) { this.length = length; } + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } + + public EntryType getType() { + return type; + } + + public void setType(EntryType type) { + this.type = type; + } } \ No newline at end of file diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/ZipEntryHeader.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/ZipEntryHeader.java index 7c880a6bea5a14cb38fa5da1a9e7a777a0ef890f..d020c8fa237577be67bf44f40af1b98852003f5b 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/ZipEntryHeader.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/zip/ZipEntryHeader.java @@ -113,6 +113,13 @@ public class ZipEntryHeader { private int length; + /** + * updateLength + */ + public void updateLength() { + length = HEADER_LENGTH + fileNameLength + extraLength; + } + /** * get Zip Entry Header * @@ -137,7 +144,7 @@ public class ZipEntryHeader { entryHeader.setUnCompressedSize(UnsignedDecimalUtil.getUnsignedInt(bf)); entryHeader.setFileNameLength(UnsignedDecimalUtil.getUnsignedShort(bf)); entryHeader.setExtraLength(UnsignedDecimalUtil.getUnsignedShort(bf)); - entryHeader.setLength(HEADER_LENGTH + entryHeader.getFileNameLength() + entryHeader.getExtraLength()); + entryHeader.updateLength(); return entryHeader; }