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