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