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;
}