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.exception.VerifyCodeSignException; 20 21 import java.nio.ByteBuffer; 22 import java.nio.ByteOrder; 23 24 /** 25 * Format of FsVerity descriptor 26 * uint8 version 27 * uint8 hashAlgorithm 28 * uint8 log2BlockSize 29 * uint8 saltSize 30 * uint32 signSize 31 * le64 dataSize 32 * uint8[64] rootHash 33 * uint8[32] salt 34 * uint32 flags 35 * uint8[4] 0 36 * uint64 treeOffset 37 * uint8[127] 0 38 * uint8 csVersion 39 * 40 * @since 2023/06/05 41 */ 42 public class FsVerityDescriptor { 43 /** 44 * fs-verity version, must be 1 45 */ 46 public static final byte VERSION = 1; 47 48 /** 49 * page size in bytes 50 */ 51 public static final int PAGE_SIZE_4K = 4096; 52 53 /** 54 * Indicating merkle tree offset is set in fs-verity descriptor 55 */ 56 public static final int FLAG_STORE_MERKLE_TREE_OFFSET = 0x1; 57 58 /** 59 * Indicating fs-verity descriptor type 60 */ 61 public static final int FS_VERITY_DESCRIPTOR_TYPE = 0x1; 62 63 /** 64 * code sign version 65 */ 66 public static final byte CODE_SIGN_VERSION = 0x1; 67 68 /** 69 * FsVerity descriptor size 70 */ 71 public static final int DESCRIPTOR_SIZE = 256; 72 73 /** 74 * root hash size 75 */ 76 public static final int ROOT_HASH_FILED_SIZE = 64; 77 78 /** 79 * salt size 80 */ 81 public static final int SALT_SIZE = 32; 82 83 /** 84 * reserved size 85 */ 86 public static final int RESERVED_SIZE_AFTER_FLAGS = 4; 87 88 /** 89 * reserved size 90 */ 91 public static final int RESERVED_SIZE_AFTER_TREE_OFFSET = 127; 92 93 private byte version; 94 95 private long fileSize; 96 97 private byte hashAlgorithm; 98 99 private byte log2BlockSize; 100 101 private byte saltSize; 102 103 private int signSize; 104 105 private byte[] salt; 106 107 private byte[] rawRootHash; 108 109 private int flags; 110 111 private long merkleTreeOffset; 112 113 private byte csVersion; 114 FsVerityDescriptor(Builder builder)115 private FsVerityDescriptor(Builder builder) { 116 this.version = builder.version; 117 this.fileSize = builder.fileSize; 118 this.hashAlgorithm = builder.hashAlgorithm; 119 this.log2BlockSize = builder.log2BlockSize; 120 this.saltSize = builder.saltSize; 121 this.signSize = builder.signSize; 122 this.salt = builder.salt; 123 this.rawRootHash = builder.rawRootHash; 124 this.flags = builder.flags; 125 this.merkleTreeOffset = builder.merkleTreeOffset; 126 this.csVersion = builder.csVersion; 127 } 128 getFileSize()129 public long getFileSize() { 130 return fileSize; 131 } 132 getMerkleTreeOffset()133 public long getMerkleTreeOffset() { 134 return merkleTreeOffset; 135 } 136 getSignSize()137 public int getSignSize() { 138 return signSize; 139 } 140 141 /** 142 * Init the FsVerityDescriptor by a byte array 143 * 144 * @param bytes Byte array representation of a FsVerityDescriptor object 145 * @return a newly created FsVerityDescriptor object 146 * @throws VerifyCodeSignException parse result invalid 147 */ fromByteArray(byte[] bytes)148 public static FsVerityDescriptor fromByteArray(byte[] bytes) throws VerifyCodeSignException { 149 ByteBuffer bf = ByteBuffer.allocate(bytes.length).order(ByteOrder.LITTLE_ENDIAN); 150 bf.put(bytes); 151 // after put, rewind is mandatory before get 152 bf.rewind(); 153 FsVerityDescriptor.Builder builder = new FsVerityDescriptor.Builder(); 154 byte inFsVersion = bf.get(); 155 if (FsVerityDescriptor.VERSION != inFsVersion) { 156 throw new VerifyCodeSignException("Invalid fs-verify descriptor version of ElfSignBlock"); 157 } 158 byte inFsHashAlgorithm = bf.get(); 159 byte inLog2BlockSize = bf.get(); 160 builder.setVersion(inFsVersion).setHashAlgorithm(inFsHashAlgorithm).setLog2BlockSize(inLog2BlockSize); 161 byte inSaltSize = bf.get(); 162 int inSignSize = bf.getInt(); 163 long inDataSize = bf.getLong(); 164 byte[] inRootHash = new byte[FsVerityDescriptor.ROOT_HASH_FILED_SIZE]; 165 bf.get(inRootHash); 166 builder.setSaltSize(inSaltSize).setSignSize(inSignSize).setFileSize(inDataSize).setRawRootHash(inRootHash); 167 byte[] inSalt = new byte[FsVerityDescriptor.SALT_SIZE]; 168 bf.get(inSalt); 169 int inFlags = bf.getInt(); 170 bf.getInt(); 171 long inTreeOffset = bf.getLong(); 172 if (inTreeOffset % PAGE_SIZE_4K != 0) { 173 throw new VerifyCodeSignException("Invalid merkle tree offset of ElfSignBlock"); 174 } 175 bf.get(new byte[FsVerityDescriptor.RESERVED_SIZE_AFTER_TREE_OFFSET]); 176 byte inCsVersion = bf.get(); 177 builder.setSalt(inSalt).setFlags(inFlags).setMerkleTreeOffset(inTreeOffset).setCsVersion(inCsVersion); 178 return builder.build(); 179 } 180 181 /** 182 * Get FsVerity descriptor bytes 183 * 184 * @return bytes of descriptor 185 * @throws FsVerityDigestException if error 186 */ toByteArray()187 public byte[] toByteArray() throws FsVerityDigestException { 188 ByteBuffer buffer = ByteBuffer.allocate(DESCRIPTOR_SIZE).order(ByteOrder.LITTLE_ENDIAN); 189 buffer.put(VERSION); 190 buffer.put(hashAlgorithm); 191 buffer.put(log2BlockSize); 192 if (this.saltSize > SALT_SIZE) { 193 throw new FsVerityDigestException("Salt is too long"); 194 } 195 buffer.put(this.saltSize); 196 buffer.putInt(signSize); 197 buffer.putLong(fileSize); 198 writeBytesWithSize(buffer, rawRootHash, ROOT_HASH_FILED_SIZE); 199 writeBytesWithSize(buffer, salt, SALT_SIZE); 200 buffer.putInt(flags); 201 writeBytesWithSize(buffer, null, RESERVED_SIZE_AFTER_FLAGS); 202 buffer.putLong(merkleTreeOffset); 203 writeBytesWithSize(buffer, null, RESERVED_SIZE_AFTER_TREE_OFFSET); 204 buffer.put(csVersion); 205 return buffer.array(); 206 } 207 208 /** 209 * Get bytes for generate digest, first byte is CODE_SIGN_VERSION, sign size is 0, last 128 bytes is 0 210 * 211 * @return bytes of descriptor 212 * @throws FsVerityDigestException if error 213 */ getByteForGenerateDigest()214 public byte[] getByteForGenerateDigest() throws FsVerityDigestException { 215 ByteBuffer buffer = ByteBuffer.allocate(DESCRIPTOR_SIZE).order(ByteOrder.LITTLE_ENDIAN); 216 buffer.put(CODE_SIGN_VERSION); 217 buffer.put(hashAlgorithm); 218 buffer.put(log2BlockSize); 219 if (this.saltSize > SALT_SIZE) { 220 throw new FsVerityDigestException("Salt is too long"); 221 } 222 buffer.put(this.saltSize); 223 buffer.putInt(0); 224 buffer.putLong(fileSize); 225 writeBytesWithSize(buffer, rawRootHash, ROOT_HASH_FILED_SIZE); 226 writeBytesWithSize(buffer, salt, SALT_SIZE); 227 buffer.putInt(flags); 228 writeBytesWithSize(buffer, null, RESERVED_SIZE_AFTER_FLAGS); 229 buffer.putLong(merkleTreeOffset); 230 return buffer.array(); 231 } 232 233 /** 234 * Write bytes to ByteBuffer with specific size 235 * 236 * @param buffer target buffer 237 * @param src bytes to write 238 * @param size size of written bytes, fill 0 if src bytes is long enough 239 */ writeBytesWithSize(ByteBuffer buffer, byte[] src, int size)240 private void writeBytesWithSize(ByteBuffer buffer, byte[] src, int size) { 241 int pos = buffer.position(); 242 if (src != null) { 243 if (src.length > size) { 244 buffer.put(src, 0, size); 245 } else { 246 buffer.put(src); 247 } 248 } 249 buffer.position(pos + size); 250 } 251 252 /** 253 * Builder of FsVerityDescriptor class 254 */ 255 public static class Builder { 256 private byte version = VERSION; 257 258 private long fileSize; 259 260 private byte hashAlgorithm; 261 262 private byte log2BlockSize; 263 264 private byte saltSize; 265 266 private int signSize; 267 268 private byte[] salt; 269 270 private byte[] rawRootHash; 271 272 private int flags; 273 274 private long merkleTreeOffset; 275 276 private byte csVersion; 277 setVersion(byte version)278 public Builder setVersion(byte version) { 279 this.version = version; 280 return this; 281 } 282 setFileSize(long fileSize)283 public Builder setFileSize(long fileSize) { 284 this.fileSize = fileSize; 285 return this; 286 } 287 setHashAlgorithm(byte hashAlgorithm)288 public Builder setHashAlgorithm(byte hashAlgorithm) { 289 this.hashAlgorithm = hashAlgorithm; 290 return this; 291 } 292 setLog2BlockSize(byte log2BlockSize)293 public Builder setLog2BlockSize(byte log2BlockSize) { 294 this.log2BlockSize = log2BlockSize; 295 return this; 296 } 297 setSignSize(int signSize)298 public Builder setSignSize(int signSize) { 299 this.signSize = signSize; 300 return this; 301 } 302 setSaltSize(byte saltSize)303 public Builder setSaltSize(byte saltSize) { 304 this.saltSize = saltSize; 305 return this; 306 } 307 setSalt(byte[] salt)308 public Builder setSalt(byte[] salt) { 309 this.salt = salt; 310 return this; 311 } 312 setRawRootHash(byte[] rawRootHash)313 public Builder setRawRootHash(byte[] rawRootHash) { 314 this.rawRootHash = rawRootHash; 315 return this; 316 } 317 setFlags(int flags)318 public Builder setFlags(int flags) { 319 this.flags = flags; 320 return this; 321 } 322 setMerkleTreeOffset(long merkleTreeOffset)323 public Builder setMerkleTreeOffset(long merkleTreeOffset) { 324 this.merkleTreeOffset = merkleTreeOffset; 325 return this; 326 } 327 setCsVersion(byte csVersion)328 public Builder setCsVersion(byte csVersion) { 329 this.csVersion = csVersion; 330 return this; 331 } 332 333 /** 334 * Create a FsVerityDescriptor object 335 * 336 * @return a FsVerityDescriptor object 337 */ build()338 public FsVerityDescriptor build() { 339 return new FsVerityDescriptor(this); 340 } 341 } 342 } 343