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.fsverity.FsVerityDescriptor; 20 import com.ohos.hapsigntool.codesigning.fsverity.FsVerityGenerator; 21 22 import java.nio.ByteBuffer; 23 import java.nio.ByteOrder; 24 import java.util.Locale; 25 26 /** 27 * Fs-verity info segment contains information of fs-verity protection 28 * More information of fs-verity can be found <a herf="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">here</a> 29 * <p> 30 * Structure 31 * <p> 32 * 1) u32 magic: magic number 33 * <p> 34 * 2) u8 version: fs-verity version 35 * <p> 36 * 3) u8 hashAlgorithm: hash algorithm to use for the Merkle tree 37 * <p> 38 * 4) u8 log2BlockSize: log2 of size of data and tree blocks 39 * <p> 40 * 5) u8[] reserved: for reservation 41 * 42 * @since 2023/09/08 43 */ 44 public class FsVerityInfoSegment { 45 /** 46 * fs-verity info segment size in bytes 47 */ 48 public static final int FS_VERITY_INFO_SEGMENT_SIZE = 64; 49 50 // lower 4 bytes of the MD5 result of string "fs-verity info segment" (1E38 31AB) 51 private static final int MAGIC = (0x1E38 << 16) + (0x31AB); 52 53 private static final int RESERVED_BYTE_ARRAY_LENGTH = 57; 54 55 private int magic = MAGIC; 56 57 private byte hashAlgorithm; 58 59 private byte version; 60 61 private byte log2BlockSize; 62 63 private byte[] reserved = new byte[RESERVED_BYTE_ARRAY_LENGTH]; 64 65 /** 66 * Default constructor 67 */ FsVerityInfoSegment()68 public FsVerityInfoSegment() { 69 } 70 FsVerityInfoSegment(byte version, byte hashAlgorithm, byte log2BlockSize)71 public FsVerityInfoSegment(byte version, byte hashAlgorithm, byte log2BlockSize) { 72 this(MAGIC, version, hashAlgorithm, log2BlockSize, new byte[RESERVED_BYTE_ARRAY_LENGTH]); 73 } 74 75 /** 76 * Constructor of FsVerityInfoSegment 77 * 78 * @param magic magic num 79 * @param version version of fs-verity 80 * @param hashAlgorithm hash algorithm to use for the Merkle tree 81 * @param log2BlockSize log2 of size of data and tree blocks 82 * @param reserved for reservation 83 */ FsVerityInfoSegment(int magic, byte version, byte hashAlgorithm, byte log2BlockSize, byte[] reserved)84 public FsVerityInfoSegment(int magic, byte version, byte hashAlgorithm, byte log2BlockSize, byte[] reserved) { 85 this.magic = magic; 86 this.version = version; 87 this.hashAlgorithm = hashAlgorithm; 88 this.log2BlockSize = log2BlockSize; 89 this.reserved = reserved; 90 } 91 size()92 public int size() { 93 return FS_VERITY_INFO_SEGMENT_SIZE; 94 } 95 96 /** 97 * Converts FsVerityInfoSegment to a newly created byte array 98 * 99 * @return Byte array representation of FsVerityInfoSegment 100 */ toByteArray()101 public byte[] toByteArray() { 102 ByteBuffer bf = ByteBuffer.allocate(FS_VERITY_INFO_SEGMENT_SIZE).order(ByteOrder.LITTLE_ENDIAN); 103 bf.putInt(this.magic); 104 bf.put(version); 105 bf.put(hashAlgorithm); 106 bf.put(log2BlockSize); 107 bf.put(reserved); 108 return bf.array(); 109 } 110 111 /** 112 * Init the FsVerityInfoSegment by a byte array 113 * 114 * @param bytes Byte array representation of a FsVerityInfoSegment object 115 * @return a newly created FsVerityInfoSegment object 116 * @throws VerifyCodeSignException parsing result invalid 117 */ fromByteArray(byte[] bytes)118 public static FsVerityInfoSegment fromByteArray(byte[] bytes) throws VerifyCodeSignException { 119 if (bytes.length != FS_VERITY_INFO_SEGMENT_SIZE) { 120 throw new VerifyCodeSignException("Invalid size of FsVerityInfoSegment"); 121 } 122 ByteBuffer bf = ByteBuffer.allocate(bytes.length).order(ByteOrder.LITTLE_ENDIAN); 123 bf.put(bytes); 124 bf.rewind(); 125 int inMagic = bf.getInt(); 126 if (inMagic != MAGIC) { 127 throw new VerifyCodeSignException("Invalid magic number of FsVerityInfoSegment"); 128 } 129 byte inVersion = bf.get(); 130 if (inVersion != FsVerityDescriptor.VERSION) { 131 throw new VerifyCodeSignException("Invalid version of FsVerityInfoSegment"); 132 } 133 byte inHashAlgorithm = bf.get(); 134 if (inHashAlgorithm != FsVerityGenerator.getFsVerityHashAlgorithm()) { 135 throw new VerifyCodeSignException("Invalid hashAlgorithm of FsVerityInfoSegment"); 136 } 137 byte inLog2BlockSize = bf.get(); 138 if (inLog2BlockSize != FsVerityGenerator.getLog2BlockSize()) { 139 throw new VerifyCodeSignException("Invalid log2BlockSize of FsVerityInfoSegment"); 140 } 141 byte[] inReservedBytes = new byte[RESERVED_BYTE_ARRAY_LENGTH]; 142 bf.get(inReservedBytes); 143 return new FsVerityInfoSegment(inMagic, inVersion, inHashAlgorithm, inLog2BlockSize, inReservedBytes); 144 } 145 146 /** 147 * Return a string representation of the object 148 * 149 * @return string representation of the object 150 */ toString()151 public String toString() { 152 return String.format(Locale.ROOT, "FsVerityInfoSeg: magic[%d], version[%d], hashAlg[%d], log2BlockSize[%d]", 153 this.magic, this.version, this.hashAlgorithm, this.log2BlockSize); 154 } 155 } 156