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.fsverity; 17 18 import com.ohos.hapsigntool.codesigning.datastructure.PageInfoExtension; 19 import com.ohos.hapsigntool.codesigning.exception.CodeSignErrMsg; 20 import com.ohos.hapsigntool.codesigning.exception.FsVerityDigestException; 21 import com.ohos.hapsigntool.codesigning.exception.PageInfoException; 22 import com.ohos.hapsigntool.codesigning.utils.DigestUtils; 23 24 import java.io.IOException; 25 import java.io.InputStream; 26 import java.security.NoSuchAlgorithmException; 27 28 /** 29 * FsVerity data generator supper class 30 * 31 * @since 2023/06/05 32 */ 33 public class FsVerityGenerator { 34 /** 35 * FsVerity hash algorithm 36 */ 37 private static final FsVerityHashAlgorithm FS_VERITY_HASH_ALGORITHM = FsVerityHashAlgorithm.SHA256; 38 39 private static final byte LOG_2_OF_FSVERITY_HASH_PAGE_SIZE = 12; 40 41 /** 42 * salt for hashing one page 43 */ 44 protected byte[] salt = null; 45 46 private byte[] fsVerityDigest = null; 47 48 private byte[] fsVerityDigestV2 = null; 49 50 private byte[] treeBytes = null; 51 52 private byte[] rootHash = null; 53 54 private PageInfoExtension pageInfoExtension; 55 56 /** 57 * Constructor for FsVerityGenerator 58 * 59 * @param pg PageInfoExtension 60 */ setPageInfoExtension(PageInfoExtension pg)61 public void setPageInfoExtension(PageInfoExtension pg) { 62 this.pageInfoExtension = pg; 63 } 64 65 /** 66 * generate merkle tree of given input 67 * 68 * @param inputStream input stream for generate merkle tree 69 * @param size total size of input stream 70 * @param fsVerityHashAlgorithm hash algorithm for FsVerity 71 * @return merkle tree 72 * @throws FsVerityDigestException if error 73 */ generateMerkleTree(InputStream inputStream, long size, FsVerityHashAlgorithm fsVerityHashAlgorithm)74 public MerkleTree generateMerkleTree(InputStream inputStream, long size, 75 FsVerityHashAlgorithm fsVerityHashAlgorithm) throws FsVerityDigestException { 76 MerkleTree merkleTree; 77 try (MerkleTreeBuilder builder = new MerkleTreeBuilder()) { 78 merkleTree = builder.generateMerkleTree(inputStream, size, fsVerityHashAlgorithm); 79 } catch (IOException e) { 80 throw new FsVerityDigestException(e.getMessage(), e); 81 } catch (NoSuchAlgorithmException e) { 82 throw new FsVerityDigestException( 83 CodeSignErrMsg.ALGORITHM_NOT_SUPPORT_ERROR.toString(fsVerityHashAlgorithm.getHashAlgorithm()), e); 84 } 85 return merkleTree; 86 } 87 88 /** 89 * generate FsVerity digest of given input 90 * 91 * @param inputStream input stream for generate FsVerity digest 92 * @param size total size of input stream 93 * @param fsvTreeOffset merkle tree raw bytes offset based on the start of file 94 * @throws FsVerityDigestException fsVerity digest error 95 * @throws PageInfoException page info error 96 */ generateFsVerityDigest(InputStream inputStream, long size, long fsvTreeOffset)97 public void generateFsVerityDigest(InputStream inputStream, long size, long fsvTreeOffset) 98 throws FsVerityDigestException, PageInfoException { 99 MerkleTree merkleTree; 100 if (size == 0) { 101 merkleTree = new MerkleTree(null, null, FS_VERITY_HASH_ALGORITHM); 102 } else { 103 merkleTree = generateMerkleTree(inputStream, size, FS_VERITY_HASH_ALGORITHM); 104 } 105 int flags = fsvTreeOffset == 0 ? 0 : FsVerityDescriptor.FLAG_STORE_MERKLE_TREE_OFFSET; 106 // sign size is 0, cs version is 0 107 FsVerityDescriptor.Builder builder = new FsVerityDescriptor.Builder().setFileSize(size) 108 .setHashAlgorithm(FS_VERITY_HASH_ALGORITHM.getId()) 109 .setLog2BlockSize(LOG_2_OF_FSVERITY_HASH_PAGE_SIZE) 110 .setSaltSize((byte) getSaltSize()) 111 .setSalt(salt) 112 .setRawRootHash(merkleTree.rootHash) 113 .setFlags(flags) 114 .setMerkleTreeOffset(fsvTreeOffset); 115 try { 116 byte[] fsVerityDescriptor = builder.build().getDiscByte(); 117 byte[] digest = DigestUtils.computeDigest(fsVerityDescriptor, FS_VERITY_HASH_ALGORITHM.getHashAlgorithm()); 118 fsVerityDigest = FsVerityDigest.getFsVerityDigest(FS_VERITY_HASH_ALGORITHM.getId(), digest); 119 } catch (NoSuchAlgorithmException e) { 120 throw new FsVerityDigestException( 121 CodeSignErrMsg.ALGORITHM_NOT_SUPPORT_ERROR.toString(FS_VERITY_HASH_ALGORITHM.getHashAlgorithm()), e); 122 } 123 if (pageInfoExtension != null && flags != 0) { 124 PageInfoExtension.valid(pageInfoExtension, size); 125 try { 126 byte[] fsVerityDescriptorV2 = builder.build() 127 .getDiscByteCsv2(pageInfoExtension.getMapOffset(), pageInfoExtension.getMapSize(), 128 pageInfoExtension.getUnitSize()); 129 byte[] digest = DigestUtils.computeDigest(fsVerityDescriptorV2, 130 FS_VERITY_HASH_ALGORITHM.getHashAlgorithm()); 131 fsVerityDigestV2 = FsVerityDigest.getFsVerityDigest(FS_VERITY_HASH_ALGORITHM.getId(), digest); 132 } catch (NoSuchAlgorithmException e) { 133 throw new FsVerityDigestException( 134 CodeSignErrMsg.ALGORITHM_NOT_SUPPORT_ERROR.toString(FS_VERITY_HASH_ALGORITHM.getHashAlgorithm()), 135 e); 136 } 137 } 138 treeBytes = merkleTree.tree; 139 rootHash = merkleTree.rootHash; 140 } 141 142 /** 143 * Get FsVerity digest 144 * 145 * @return bytes of FsVerity digest 146 */ getFsVerityDigest()147 public byte[] getFsVerityDigest() { 148 return fsVerityDigest; 149 } 150 151 /** 152 * Get FsVerity digest 153 * 154 * @return bytes of FsVerity digest 155 */ getFsVerityDigestV2()156 public byte[] getFsVerityDigestV2() { 157 return fsVerityDigestV2; 158 } 159 160 /** 161 * Get merkle tree in bytes 162 * 163 * @return bytes of merkle tree 164 */ getTreeBytes()165 public byte[] getTreeBytes() { 166 return treeBytes; 167 } 168 169 /** 170 * Get merkle tree rootHash in bytes 171 * 172 * @return bytes of merkle tree rootHash 173 */ getRootHash()174 public byte[] getRootHash() { 175 return rootHash; 176 } 177 getSalt()178 public byte[] getSalt() { 179 return salt; 180 } 181 182 /** 183 * Returns byte size of salt 184 * 185 * @return byte size of salt 186 */ getSaltSize()187 public int getSaltSize() { 188 return this.salt == null ? 0 : this.salt.length; 189 } 190 191 /** 192 * Returns the id of fs-verity hash algorithm 193 * 194 * @return fs-verity hash algorithm id 195 */ getFsVerityHashAlgorithm()196 public static byte getFsVerityHashAlgorithm() { 197 return FS_VERITY_HASH_ALGORITHM.getId(); 198 } 199 200 /** 201 * Returns the log2 of size of data and tree blocks 202 * 203 * @return log2 of size of data and tree blocks 204 */ getLog2BlockSize()205 public static byte getLog2BlockSize() { 206 return LOG_2_OF_FSVERITY_HASH_PAGE_SIZE; 207 } 208 } 209