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