1 /* 2 * Copyright (c) 2023-2023 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.PageInfoException; 19 import com.ohos.hapsigntool.codesigning.exception.VerifyCodeSignException; 20 import com.ohos.hapsigntool.utils.LogUtils; 21 22 import java.nio.ByteBuffer; 23 import java.nio.ByteOrder; 24 import java.util.ArrayList; 25 import java.util.Arrays; 26 import java.util.List; 27 import java.util.Locale; 28 29 /** 30 * Sign info represents information after signing a file, including signature, merkle tree. 31 * Structure: 32 * <p> 33 * 1) u32 saltSize: byte size of salt 34 * <p> 35 * 2) u32 sigSize: byte size of signature 36 * <p> 37 * 3) u32 flags: reserved flags 38 * <p> 39 * 4) u64 dataSize: byte size of data being signed 40 * <p> 41 * 5) u8[32] salt: salt used in signing 42 * <p> 43 * 6) u32 extensionNum: number of extension 44 * <p> 45 * 7) u32 extensionOffset 46 * <p> 47 * 8) u8[] signature: signature of the data 48 * <p> 49 * MerkleTree is represented as an extension of the sign info. 50 * Its structure is defined in MerkleTreeExtension.java 51 * 52 * @since 2023/09/08 53 */ 54 public class SignInfo { 55 /** 56 * merkle tree extension is included in sign info 57 */ 58 public static final int FLAG_MERKLE_TREE_INCLUDED = 0x1; 59 60 /** 61 * maximum of extension number 62 */ 63 public static final int MAX_EXTENSION_NUM = 2; 64 65 private static final LogUtils LOGGER = new LogUtils(SignInfo.class); 66 67 /** 68 * sign info structure without signature in bytes, refer to toByteArray() method 69 */ 70 private static final int SIGN_INFO_SIZE_WITHOUT_SIGNATURE = 60; 71 72 private static final int SALT_BUFFER_LENGTH = 32; 73 74 private static final int SIGNATURE_ALIGNMENT = 4; 75 76 private int saltSize; 77 78 private int sigSize; 79 80 private int flags; 81 82 private long dataSize; 83 84 private byte[] salt; 85 86 private int extensionNum; 87 88 private int extensionOffset; 89 90 private byte[] signature; 91 92 private byte[] zeroPadding; 93 94 // temporary, use list instead 95 private List<Extension> extensionList = new ArrayList<>(); 96 97 /** 98 * Constructor for SignInfo 99 * 100 * @param saltSize byte size of salt 101 * @param flags reserved flags 102 * @param dataSize byte size of data being signed 103 * @param salt salt in byte array representation 104 * @param sig signature after signing the data in byte array representation 105 */ SignInfo(int saltSize, int flags, long dataSize, byte[] salt, byte[] sig)106 public SignInfo(int saltSize, int flags, long dataSize, byte[] salt, byte[] sig) { 107 this.saltSize = saltSize; 108 this.flags = flags; 109 this.dataSize = dataSize; 110 if (salt == null) { 111 this.salt = new byte[SALT_BUFFER_LENGTH]; 112 } else { 113 this.salt = salt; 114 } 115 this.signature = sig; 116 this.sigSize = sig == null ? 0 : sig.length; 117 // align for extension after signature 118 this.zeroPadding = new byte[(SIGNATURE_ALIGNMENT - (this.sigSize % SIGNATURE_ALIGNMENT)) % SIGNATURE_ALIGNMENT]; 119 this.extensionOffset = SIGN_INFO_SIZE_WITHOUT_SIGNATURE + sigSize + this.zeroPadding.length; 120 } 121 122 /** 123 * Constructor by a SignInfoBuilder 124 * 125 * @param builder SignInfoBuilder 126 */ SignInfo(SignInfoBuilder builder)127 private SignInfo(SignInfoBuilder builder) { 128 this.saltSize = builder.saltSize; 129 this.sigSize = builder.sigSize; 130 this.flags = builder.flags; 131 this.dataSize = builder.dataSize; 132 this.salt = builder.salt; 133 this.extensionNum = builder.extensionNum; 134 this.extensionOffset = builder.extensionOffset; 135 this.signature = builder.signature; 136 this.zeroPadding = builder.zeroPadding; 137 this.extensionList = builder.extensionList; 138 } 139 140 /** 141 * Add one Extension into SignInfo Object 142 * 143 * @param extension Extension object 144 */ addExtension(Extension extension)145 public void addExtension(Extension extension) { 146 this.extensionList.add(extension); 147 this.extensionNum = this.extensionList.size(); 148 } 149 150 /** 151 * Get Extension from SignInfo based on extension type 152 * 153 * @param type extension type 154 * @return Extension object 155 */ getExtensionByType(int type)156 public Extension getExtensionByType(int type) { 157 for (Extension ext : this.extensionList) { 158 if (ext.isType(type)) { 159 return ext; 160 } 161 } 162 return null; 163 } 164 165 /** 166 * Returns extensionNum 167 * 168 * @return extensionNum 169 */ getExtensionNum()170 public int getExtensionNum() { 171 return extensionNum; 172 } 173 getSignature()174 public byte[] getSignature() { 175 return signature; 176 } 177 getDataSize()178 public long getDataSize() { 179 return dataSize; 180 } 181 182 /** 183 * Returns byte size of SignInfo object 184 * 185 * @return byte size of SignInfo object 186 */ size()187 public int size() { 188 int blockSize = SIGN_INFO_SIZE_WITHOUT_SIGNATURE + this.signature.length + this.zeroPadding.length; 189 for (Extension ext : this.extensionList) { 190 blockSize += ext.size(); 191 } 192 return blockSize; 193 } 194 195 /** 196 * Converts SignInfo to a newly created byte array 197 * 198 * @return Byte array representation of SignInfo 199 */ toByteArray()200 public byte[] toByteArray() { 201 ByteBuffer bf = ByteBuffer.allocate(this.size()).order(ByteOrder.LITTLE_ENDIAN); 202 bf.putInt(this.saltSize); 203 bf.putInt(this.sigSize); 204 bf.putInt(this.flags); 205 bf.putLong(this.dataSize); 206 bf.put(this.salt); 207 bf.putInt(this.extensionNum); 208 bf.putInt(this.extensionOffset); 209 bf.put(this.signature); 210 bf.put(this.zeroPadding); 211 // put extension 212 for (Extension ext : this.extensionList) { 213 bf.put(ext.toByteArray()); 214 } 215 return bf.array(); 216 } 217 218 /** 219 * Init the SignInfo by a byte array 220 * 221 * @param bytes Byte array representation of a SignInfo object 222 * @return a newly created SignInfo object 223 * @throws VerifyCodeSignException parsing result invalid 224 */ fromByteArray(byte[] bytes)225 public static SignInfo fromByteArray(byte[] bytes) throws VerifyCodeSignException { 226 ByteBuffer bf = ByteBuffer.allocate(bytes.length).order(ByteOrder.LITTLE_ENDIAN); 227 bf.put(bytes); 228 bf.rewind(); 229 int inSaltSize = bf.getInt(); 230 if (inSaltSize < 0) { 231 throw new VerifyCodeSignException("Invalid saltSize of SignInfo"); 232 } 233 int inSigSize = bf.getInt(); 234 if (inSigSize < 0) { 235 throw new VerifyCodeSignException("Invalid sigSize of SignInfo"); 236 } 237 int inFlags = bf.getInt(); 238 if (inFlags != 0 && inFlags != FLAG_MERKLE_TREE_INCLUDED) { 239 throw new VerifyCodeSignException("Invalid flags of SignInfo"); 240 } 241 long inDataSize = bf.getLong(); 242 if (inDataSize < 0) { 243 throw new VerifyCodeSignException("Invalid dataSize of SignInfo"); 244 } 245 byte[] inSalt = new byte[SALT_BUFFER_LENGTH]; 246 bf.get(inSalt); 247 int inExtensionNum = bf.getInt(); 248 if (inExtensionNum < 0 || inExtensionNum > MAX_EXTENSION_NUM) { 249 LOGGER.info("The signature information may be generated by an new tool, extensionNum {} of SignInfo", 250 inExtensionNum); 251 } 252 int inExtensionOffset = bf.getInt(); 253 if (inExtensionOffset < 0 || inExtensionOffset % 4 != 0) { 254 throw new VerifyCodeSignException("Invalid extensionOffset of SignInfo"); 255 } 256 byte[] inSignature = new byte[inSigSize]; 257 bf.get(inSignature); 258 byte[] inZeroPadding = new byte[(SIGNATURE_ALIGNMENT - (inSigSize % SIGNATURE_ALIGNMENT)) 259 % SIGNATURE_ALIGNMENT]; 260 bf.get(inZeroPadding); 261 // parse merkle tree extension 262 List<Extension> inExtensionList = parseExtensionList(bf, inExtensionNum, inDataSize); 263 return new SignInfoBuilder().setSaltSize(inSaltSize) 264 .setSigSize(inSigSize) 265 .setFlags(inFlags) 266 .setDataSize(inDataSize) 267 .setSalt(inSalt) 268 .setExtensionNum(inExtensionNum) 269 .setExtensionOffset(inExtensionOffset) 270 .setSignature(inSignature) 271 .setZeroPadding(inZeroPadding) 272 .setExtensionList(inExtensionList) 273 .build(); 274 } 275 parseExtensionList(ByteBuffer bf, int inExtensionNum, long inDataSize)276 private static List<Extension> parseExtensionList(ByteBuffer bf, int inExtensionNum, long inDataSize) 277 throws VerifyCodeSignException { 278 List<Extension> inExtensionList = new ArrayList<>(); 279 for (int i = 0; i < inExtensionNum; i++) { 280 int extensionType = bf.getInt(); 281 if (extensionType == MerkleTreeExtension.MERKLE_TREE_INLINED) { 282 // parse merkle tree extension 283 int extensionSize = bf.getInt(); 284 if (extensionSize != (MerkleTreeExtension.MERKLE_TREE_EXTENSION_DATA_SIZE)) { 285 throw new VerifyCodeSignException("Invalid MerkleTree extensionSize of SignInfo"); 286 } 287 byte[] merkleTreeExtension = new byte[extensionSize]; 288 bf.get(merkleTreeExtension); 289 inExtensionList.add(MerkleTreeExtension.fromByteArray(merkleTreeExtension)); 290 } else if (extensionType == PageInfoExtension.PAGE_INFO_INLINED) { 291 // parse page info extension 292 int extensionSize = bf.getInt(); 293 if (extensionSize < (PageInfoExtension.PAGE_INFO_EXTENSION_DATA_SIZE_WITHOUT_SIGN)) { 294 throw new VerifyCodeSignException("Invalid PageInfo extensionSize of SignInfo"); 295 } 296 byte[] extensionBytes = new byte[extensionSize]; 297 bf.get(extensionBytes); 298 PageInfoExtension pageInfoExtension = PageInfoExtension.fromByteArray(extensionBytes); 299 try { 300 PageInfoExtension.valid(pageInfoExtension, inDataSize); 301 } catch (PageInfoException e) { 302 throw new VerifyCodeSignException(e.getMessage()); 303 } 304 inExtensionList.add(pageInfoExtension); 305 } else { 306 LOGGER.info("Invalid extensionType {} of SignInfo", extensionType); 307 } 308 } 309 return inExtensionList; 310 } 311 312 /** 313 * Return a string representation of the object 314 * 315 * @return string representation of the object 316 */ toString()317 public String toString() { 318 String str = String.format(Locale.ROOT, "SignInfo: saltSize[%d], sigSize[%d]," 319 + "flags[%d], dataSize[%d], salt[%s], zeroPad[%s], extNum[%d], extOffset[%d]", 320 this.saltSize, this.sigSize, this.flags, this.dataSize, Arrays.toString(this.salt), 321 Arrays.toString(this.zeroPadding), this.extensionNum, this.extensionOffset); 322 if (this.getExtensionByType(MerkleTreeExtension.MERKLE_TREE_INLINED) != null) { 323 str += String.format(Locale.ROOT, "SignInfo.merkleTreeExtension[%s]", 324 this.getExtensionByType(MerkleTreeExtension.MERKLE_TREE_INLINED).toString()); 325 } 326 return str; 327 } 328 329 /** 330 * Builder of SignInfo object 331 */ 332 public static class SignInfoBuilder { 333 private int saltSize; 334 335 private int sigSize; 336 337 private int flags; 338 339 private long dataSize; 340 341 private byte[] salt; 342 343 private int extensionNum; 344 345 private int extensionOffset; 346 347 private byte[] signature; 348 349 private byte[] zeroPadding; 350 351 // temporary, use list instead 352 private List<Extension> extensionList = new ArrayList<>(); 353 354 /** 355 * set saltSize 356 * 357 * @param saltSize saltSize 358 * @return SignInfoBuilder 359 */ setSaltSize(int saltSize)360 public SignInfoBuilder setSaltSize(int saltSize) { 361 this.saltSize = saltSize; 362 return this; 363 } 364 365 /** 366 * set sigSize 367 * 368 * @param sigSize sigSize 369 * @return SignInfoBuilder 370 */ setSigSize(int sigSize)371 public SignInfoBuilder setSigSize(int sigSize) { 372 this.sigSize = sigSize; 373 return this; 374 } 375 376 /** 377 * set flags 378 * 379 * @param flags flags 380 * @return SignInfoBuilder 381 */ setFlags(int flags)382 public SignInfoBuilder setFlags(int flags) { 383 this.flags = flags; 384 return this; 385 } 386 387 /** 388 * set dataSize 389 * 390 * @param dataSize dataSize 391 * @return SignInfoBuilder 392 */ setDataSize(long dataSize)393 public SignInfoBuilder setDataSize(long dataSize) { 394 this.dataSize = dataSize; 395 return this; 396 } 397 398 /** 399 * set salt 400 * 401 * @param salt salt 402 * @return SignInfoBuilder 403 */ setSalt(byte[] salt)404 public SignInfoBuilder setSalt(byte[] salt) { 405 this.salt = salt; 406 return this; 407 } 408 409 /** 410 * set extensionNum 411 * 412 * @param extensionNum extensionNum 413 * @return SignInfoBuilder 414 */ setExtensionNum(int extensionNum)415 public SignInfoBuilder setExtensionNum(int extensionNum) { 416 this.extensionNum = extensionNum; 417 return this; 418 } 419 420 /** 421 * set extensionOffset 422 * 423 * @param extensionOffset extensionOffset 424 * @return SignInfoBuilder 425 */ setExtensionOffset(int extensionOffset)426 public SignInfoBuilder setExtensionOffset(int extensionOffset) { 427 this.extensionOffset = extensionOffset; 428 return this; 429 } 430 431 /** 432 * set signature 433 * 434 * @param signature signature 435 * @return SignInfoBuilder 436 */ setSignature(byte[] signature)437 public SignInfoBuilder setSignature(byte[] signature) { 438 this.signature = signature; 439 return this; 440 } 441 442 /** 443 * set zeroPadding 444 * 445 * @param zeroPadding zeroPadding 446 * @return SignInfoBuilder 447 */ setZeroPadding(byte[] zeroPadding)448 public SignInfoBuilder setZeroPadding(byte[] zeroPadding) { 449 this.zeroPadding = zeroPadding; 450 return this; 451 } 452 453 /** 454 * set extensionList 455 * 456 * @param extensionList extensionList 457 * @return SignInfoBuilder 458 */ setExtensionList(List<Extension> extensionList)459 public SignInfoBuilder setExtensionList(List<Extension> extensionList) { 460 this.extensionList = extensionList; 461 return this; 462 } 463 464 /** 465 * return a SignInfo object 466 * 467 * @return SignInfo object 468 */ build()469 public SignInfo build() { 470 return new SignInfo(this); 471 } 472 } 473 } 474