• 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.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