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