• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package com.ohos.hapsigntool.codesigning.datastructure;
17 
18 import com.ohos.hapsigntool.codesigning.exception.CodeSignErrMsg;
19 import com.ohos.hapsigntool.codesigning.exception.PageInfoException;
20 import com.ohos.hapsigntool.codesigning.exception.VerifyCodeSignException;
21 import com.ohos.hapsigntool.codesigning.utils.NumberUtils;
22 
23 import java.nio.ByteBuffer;
24 import java.nio.ByteOrder;
25 import java.util.Locale;
26 
27 /**
28  * Pages info extension is a type of Extension to store bitmap file's information, i.e. size and offset, ect.
29  * <p>
30  * structure
31  * <p>
32  * 1) u32 type 0x2
33  * <p>
34  * 2) u64 mapOffset: offset of the bitmap by the start of the file.
35  * <p>
36  * 3) u64 mapSize: the bit size of bitmap.
37  * <p>
38  * 4) u8  unitSize: unit size corresponding to each page, default 4 .
39  * <p>
40  * 5) u8[3] reserved:
41  * <p>
42  * 6) u32 signSize: signature size
43  * <p>
44  * 7) u8[] signature: signature of the data
45  *
46  * @since 2024/07/01
47  */
48 public class PageInfoExtension extends Extension {
49     /**
50      * Type of PageInfoExtension
51      */
52     public static final int PAGE_INFO_INLINED = 0x2;
53 
54     /**
55      * Byte size of PageInfoExtension
56      */
57     public static final int PAGE_INFO_EXTENSION_DATA_SIZE_WITHOUT_SIGN = 24;
58 
59     /**
60      * default unit size
61      */
62     public static final int DEFAULT_UNIT_SIZE = 4;
63 
64     private static final int RESERVED_SIZE = 3;
65 
66     private static final int SIGNATURE_ALIGNMENT = 4;
67 
68     private long mapOffset;
69 
70     private long mapSize;
71 
72     private byte unitSize;
73 
74     private byte[] reserved = new byte[RESERVED_SIZE];
75 
76     private int signSize;
77 
78     private byte[] signature = new byte[0];
79 
80     private byte[] zeroPadding = new byte[0];
81 
82     /**
83      * Constructor for PageInfoExtension
84      *
85      * @param mapOffset bitmap offset
86      * @param mapSize   bit size
87      */
PageInfoExtension(long mapOffset, long mapSize)88     public PageInfoExtension(long mapOffset, long mapSize) {
89         super(PAGE_INFO_INLINED, PAGE_INFO_EXTENSION_DATA_SIZE_WITHOUT_SIGN);
90         this.mapOffset = mapOffset;
91         this.mapSize = mapSize;
92         unitSize = DEFAULT_UNIT_SIZE;
93     }
94 
setSignature(byte[] signature)95     public void setSignature(byte[] signature) {
96         if (signature != null) {
97             this.signSize = signature.length;
98             this.signature = signature;
99             this.zeroPadding = new byte[(SIGNATURE_ALIGNMENT - (signSize % SIGNATURE_ALIGNMENT)) % SIGNATURE_ALIGNMENT];
100         }
101         super.setSize(size() - Extension.EXTENSION_HEADER_SIZE);
102     }
103 
getMapOffset()104     public long getMapOffset() {
105         return mapOffset;
106     }
107 
getMapSize()108     public long getMapSize() {
109         return mapSize;
110     }
111 
getUnitSize()112     public byte getUnitSize() {
113         return unitSize;
114     }
115 
116     /**
117      * Byte size of PageInfoExtension
118      *
119      * @return Byte size of PageInfoExtension
120      */
121     @Override
size()122     public int size() {
123         return Extension.EXTENSION_HEADER_SIZE + PAGE_INFO_EXTENSION_DATA_SIZE_WITHOUT_SIGN + signSize
124             + zeroPadding.length;
125     }
126 
127     /**
128      * Converts PageInfoExtension to a newly created byte array
129      *
130      * @return Byte array representation of PageInfoExtension
131      */
132     @Override
toByteArray()133     public byte[] toByteArray() {
134         ByteBuffer bf = ByteBuffer.allocate(size()).order(ByteOrder.LITTLE_ENDIAN);
135         bf.put(super.toByteArray());
136         bf.putLong(this.mapOffset);
137         bf.putLong(this.mapSize);
138         bf.put(this.unitSize);
139         bf.put(this.reserved);
140         bf.putInt(this.signSize);
141         bf.put(this.signature);
142         bf.put(this.zeroPadding);
143         return bf.array();
144     }
145 
146     /**
147      * Init the PageInfoExtension by a byte array
148      *
149      * @param bytes Byte array representation of a PageInfoExtension object
150      * @return a newly created PageInfoExtension object
151      * @throws VerifyCodeSignException parse result invalid
152      */
fromByteArray(byte[] bytes)153     public static PageInfoExtension fromByteArray(byte[] bytes) throws VerifyCodeSignException {
154         ByteBuffer bf = ByteBuffer.allocate(bytes.length).order(ByteOrder.LITTLE_ENDIAN);
155         bf.put(bytes);
156         bf.rewind();
157         long inMapOffset = bf.getLong();
158         if (!NumberUtils.isMultiple4K(inMapOffset)) {
159             throw new VerifyCodeSignException("mapOffset is not a multiple of 4096");
160         }
161         long inMapSize = bf.getLong();
162         byte inUnitSize = bf.get();
163         if (inMapSize % inUnitSize != 0) {
164             throw new VerifyCodeSignException("mapSize is not a multiple of unitSize");
165         }
166         bf.get(new byte[RESERVED_SIZE]);
167         int inSignSize = bf.getInt();
168         byte[] inSignature = new byte[inSignSize];
169         bf.get(inSignature);
170         PageInfoExtension extension = new PageInfoExtension(inMapOffset, inMapSize);
171         extension.unitSize = inUnitSize;
172         extension.setSignature(inSignature);
173         return extension;
174     }
175 
176     /**
177      * validate PageInfoExtension with dataSize
178      *
179      * @param pgExtension  PageInfoExtension
180      * @param dataSize     signed data size
181      * @return true while PageInfoExtension filed value is valid
182      * @throws PageInfoException error while filed value is invalid
183      */
valid(PageInfoExtension pgExtension, long dataSize)184     public static boolean valid(PageInfoExtension pgExtension, long dataSize) throws PageInfoException {
185         if (!NumberUtils.isMultiple4K(pgExtension.getMapOffset())) {
186             throw new PageInfoException(CodeSignErrMsg.PAGE_INFO_ERROR.toString(
187                 "Invalid bitmapOff { " + pgExtension.getMapOffset() + " }, not a multiple of 4096"));
188         }
189         if (pgExtension.getUnitSize() != PageInfoExtension.DEFAULT_UNIT_SIZE) {
190             throw new PageInfoException(
191                 CodeSignErrMsg.PAGE_INFO_ERROR.toString("Invalid page info unitSize : " + pgExtension.getUnitSize()));
192         }
193         if (pgExtension.getMapOffset() < 0 || pgExtension.getMapSize() < 0) {
194             throw new PageInfoException(
195                 CodeSignErrMsg.PAGE_INFO_ERROR.toString("Page info offset or size is negative number"));
196         }
197         if (pgExtension.getMapSize() % pgExtension.getUnitSize() != 0) {
198             throw new PageInfoException(
199                 CodeSignErrMsg.PAGE_INFO_ERROR.toString("Page info size is not multiple of unit"));
200         }
201 
202         if (pgExtension.getMapOffset() > dataSize - pgExtension.getMapSize() / Byte.SIZE) {
203             throw new PageInfoException(CodeSignErrMsg.PAGE_INFO_ERROR.toString("Page info is out of dataSize"));
204         }
205         if (pgExtension.getMapSize() / pgExtension.getUnitSize() >= dataSize / CodeSignBlock.PAGE_SIZE_4K) {
206             throw new PageInfoException(
207                 CodeSignErrMsg.PAGE_INFO_ERROR.toString("Page info size is not less than data page "));
208         }
209         return true;
210     }
211 
212     @Override
toString()213     public String toString() {
214         return String.format(Locale.ROOT,
215             "PageInfoExtension: size[%d], mapOffset[%d], mapSize[%d], unitSize[%d], signSize[%d]", size(),
216             this.mapOffset, this.mapSize, this.unitSize, this.signSize);
217     }
218 }
219