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 import com.ohos.hapsigntool.codesigning.utils.NumberUtils; 20 21 import java.nio.ByteBuffer; 22 import java.nio.ByteOrder; 23 import java.util.Arrays; 24 import java.util.Locale; 25 26 /** 27 * Merkle tree extension is a type of Extension to store a merkle tree's information, i.e. size and root hash, ect. 28 * <p> 29 * structure 30 * <p> 31 * 1) u32 type 32 * <p> 33 * 2) u64 merkleTreeSize: the size of merkle tree 34 * <p> 35 * 3) u64 merkleTreeOffset: offset of the merkle tree by the start of the file. 36 * <p> 37 * 4) u8[64] rootHash: merkle tree root hash 38 * 39 * @since 2023/09/08 40 */ 41 public class MerkleTreeExtension extends Extension { 42 /** 43 * Type of MerkleTreeExtension 44 */ 45 public static final int MERKLE_TREE_INLINED = 0x1; 46 47 /** 48 * Byte size of MerkleTreeExtension including merkleTreeSize, offset and root hash. 49 */ 50 public static final int MERKLE_TREE_EXTENSION_DATA_SIZE = 80; 51 52 private static final int ROOT_HASH_SIZE = 64; 53 54 private final long merkleTreeSize; 55 56 private long merkleTreeOffset; 57 58 private byte[] rootHash; 59 60 /** 61 * Constructor for MerkleTreeExtension 62 * 63 * @param merkleTreeSize Byte array representation of merkle tree 64 * @param merkleTreeOffset merkle tree offset based on file start 65 * @param rootHash Root hash of the merkle tree 66 */ MerkleTreeExtension(long merkleTreeSize, long merkleTreeOffset, byte[] rootHash)67 public MerkleTreeExtension(long merkleTreeSize, long merkleTreeOffset, byte[] rootHash) { 68 super(MERKLE_TREE_INLINED, MERKLE_TREE_EXTENSION_DATA_SIZE); 69 this.merkleTreeSize = merkleTreeSize; 70 this.merkleTreeOffset = merkleTreeOffset; 71 if (rootHash == null) { 72 this.rootHash = new byte[ROOT_HASH_SIZE]; 73 } else { 74 this.rootHash = Arrays.copyOf(rootHash, ROOT_HASH_SIZE); 75 } 76 } 77 78 @Override size()79 public int size() { 80 return Extension.EXTENSION_HEADER_SIZE + MERKLE_TREE_EXTENSION_DATA_SIZE; 81 } 82 getMerkleTreeSize()83 public long getMerkleTreeSize() { 84 return merkleTreeSize; 85 } 86 getMerkleTreeOffset()87 public long getMerkleTreeOffset() { 88 return merkleTreeOffset; 89 } 90 setMerkleTreeOffset(long offset)91 public void setMerkleTreeOffset(long offset) { 92 this.merkleTreeOffset = offset; 93 } 94 95 /** 96 * Converts MerkleTreeExtension to a newly created byte array 97 * 98 * @return Byte array representation of MerkleTreeExtension 99 */ 100 @Override toByteArray()101 public byte[] toByteArray() { 102 ByteBuffer bf = ByteBuffer.allocate(size()).order(ByteOrder.LITTLE_ENDIAN); 103 bf.put(super.toByteArray()); 104 bf.putLong(this.merkleTreeSize); 105 bf.putLong(this.merkleTreeOffset); 106 bf.put(this.rootHash); 107 return bf.array(); 108 } 109 110 /** 111 * Init the MerkleTreeExtension by a byte array 112 * 113 * @param bytes Byte array representation of a MerkleTreeExtension object 114 * @return a newly created MerkleTreeExtension object 115 * @throws VerifyCodeSignException parsing result invalid 116 */ fromByteArray(byte[] bytes)117 public static MerkleTreeExtension fromByteArray(byte[] bytes) throws VerifyCodeSignException { 118 ByteBuffer bf = ByteBuffer.allocate(bytes.length).order(ByteOrder.LITTLE_ENDIAN); 119 bf.put(bytes); 120 bf.rewind(); 121 long inMerkleTreeSize = bf.getLong(); 122 if (!NumberUtils.isMultiple4K(inMerkleTreeSize)) { 123 throw new VerifyCodeSignException("merkleTreeSize is not a multiple of 4096"); 124 } 125 long inMerkleTreeOffset = bf.getLong(); 126 if (!NumberUtils.isMultiple4K(inMerkleTreeOffset)) { 127 throw new VerifyCodeSignException("merkleTreeOffset is not a aligned to 4096"); 128 } 129 byte[] inRootHash = new byte[ROOT_HASH_SIZE]; 130 bf.get(inRootHash); 131 return new MerkleTreeExtension(inMerkleTreeSize, inMerkleTreeOffset, inRootHash); 132 } 133 134 /** 135 * Return a string representation of the object 136 * 137 * @return string representation of the object 138 */ 139 @Override toString()140 public String toString() { 141 return String.format(Locale.ROOT, "MerkleTreeExtension: merkleTreeSize[%d], merkleTreeOffset[%d]," 142 + " rootHash[%s]", this.merkleTreeSize, this.merkleTreeOffset, Arrays.toString(this.rootHash)); 143 } 144 } 145