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.Arrays; 23 import java.util.Locale; 24 25 /** 26 * Merkle tree extension is a type of Extension to store a merkle tree's information, i.e. size and root hash, ect. 27 * <p> 28 * structure 29 * <p> 30 * 1) u32 type 31 * <p> 32 * 2) u64 merkleTreeSize: the size of merkle tree 33 * <p> 34 * 3) u64 merkleTreeOffset: offset of the merkle tree by the start of the file. 35 * <p> 36 * 4) u8[64] rootHash: merkle tree root hash 37 * 38 * @since 2023/09/08 39 */ 40 public class MerkleTreeExtension extends Extension { 41 /** 42 * Type of MerkleTreeExtension 43 */ 44 public static final int MERKLE_TREE_INLINED = 0x1; 45 46 /** 47 * Byte size of MerkleTreeExtension including merkleTreeSize, offset and root hash. 48 */ 49 public static final int MERKLE_TREE_EXTENSION_DATA_SIZE = 80; 50 51 private static final int ROOT_HASH_SIZE = 64; 52 53 private final long merkleTreeSize; 54 55 private long merkleTreeOffset; 56 57 private byte[] rootHash; 58 59 /** 60 * Constructor for MerkleTreeExtension 61 * 62 * @param merkleTreeSize Byte array representation of merkle tree 63 * @param merkleTreeOffset merkle tree offset based on file start 64 * @param rootHash Root hash of the merkle tree 65 */ MerkleTreeExtension(long merkleTreeSize, long merkleTreeOffset, byte[] rootHash)66 public MerkleTreeExtension(long merkleTreeSize, long merkleTreeOffset, byte[] rootHash) { 67 super(MERKLE_TREE_INLINED, MERKLE_TREE_EXTENSION_DATA_SIZE); 68 this.merkleTreeSize = merkleTreeSize; 69 this.merkleTreeOffset = merkleTreeOffset; 70 if (rootHash == null) { 71 this.rootHash = new byte[ROOT_HASH_SIZE]; 72 } else { 73 this.rootHash = Arrays.copyOf(rootHash, ROOT_HASH_SIZE); 74 } 75 } 76 77 @Override size()78 public int size() { 79 return Extension.EXTENSION_HEADER_SIZE + MERKLE_TREE_EXTENSION_DATA_SIZE; 80 } 81 getMerkleTreeSize()82 public long getMerkleTreeSize() { 83 return merkleTreeSize; 84 } 85 getMerkleTreeOffset()86 public long getMerkleTreeOffset() { 87 return merkleTreeOffset; 88 } 89 setMerkleTreeOffset(long offset)90 public void setMerkleTreeOffset(long offset) { 91 this.merkleTreeOffset = offset; 92 } 93 94 /** 95 * Converts MerkleTreeExtension to a newly created byte array 96 * 97 * @return Byte array representation of MerkleTreeExtension 98 */ 99 @Override toByteArray()100 public byte[] toByteArray() { 101 ByteBuffer bf = ByteBuffer.allocate(Extension.EXTENSION_HEADER_SIZE + MERKLE_TREE_EXTENSION_DATA_SIZE) 102 .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 (inMerkleTreeSize % CodeSignBlock.PAGE_SIZE_4K != 0) { 123 throw new VerifyCodeSignException("merkleTreeSize is not a multiple of 4096"); 124 } 125 long inMerkleTreeOffset = bf.getLong(); 126 if (inMerkleTreeOffset % CodeSignBlock.PAGE_SIZE_4K != 0) { 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