• 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.sign;
17 
18 import com.ohos.hapsigntool.codesigning.datastructure.CodeSignBlock;
19 import com.ohos.hapsigntool.codesigning.datastructure.ElfSignBlock;
20 import com.ohos.hapsigntool.codesigning.datastructure.Extension;
21 import com.ohos.hapsigntool.codesigning.datastructure.FsVerityInfoSegment;
22 import com.ohos.hapsigntool.codesigning.datastructure.MerkleTreeExtension;
23 import com.ohos.hapsigntool.codesigning.datastructure.PageInfoExtension;
24 import com.ohos.hapsigntool.codesigning.datastructure.SignInfo;
25 import com.ohos.hapsigntool.codesigning.elf.ElfHeader;
26 import com.ohos.hapsigntool.codesigning.exception.CodeSignErrMsg;
27 import com.ohos.hapsigntool.codesigning.exception.CodeSignException;
28 import com.ohos.hapsigntool.codesigning.exception.FsVerityDigestException;
29 import com.ohos.hapsigntool.codesigning.exception.PageInfoException;
30 import com.ohos.hapsigntool.codesigning.fsverity.FsVerityDescriptor;
31 import com.ohos.hapsigntool.codesigning.fsverity.FsVerityDescriptorWithSign;
32 import com.ohos.hapsigntool.codesigning.fsverity.FsVerityGenerator;
33 import com.ohos.hapsigntool.codesigning.utils.HapUtils;
34 import com.ohos.hapsigntool.codesigning.utils.NumberUtils;
35 import com.ohos.hapsigntool.entity.Pair;
36 import com.ohos.hapsigntool.error.HapFormatException;
37 import com.ohos.hapsigntool.error.ProfileException;
38 import com.ohos.hapsigntool.hap.config.SignerConfig;
39 import com.ohos.hapsigntool.signer.LocalSigner;
40 import com.ohos.hapsigntool.utils.FileUtils;
41 import com.ohos.hapsigntool.utils.LogUtils;
42 import com.ohos.hapsigntool.utils.StringUtils;
43 import com.ohos.hapsigntool.zip.EntryType;
44 import com.ohos.hapsigntool.zip.Zip;
45 import com.ohos.hapsigntool.zip.ZipEntry;
46 import com.ohos.hapsigntool.zip.ZipEntryHeader;
47 
48 import java.io.File;
49 import java.io.FileInputStream;
50 import java.io.FileOutputStream;
51 import java.io.IOException;
52 import java.io.InputStream;
53 import java.util.ArrayList;
54 import java.util.Enumeration;
55 import java.util.List;
56 import java.util.Locale;
57 import java.util.Map;
58 import java.util.jar.JarEntry;
59 import java.util.jar.JarFile;
60 import java.util.stream.Collectors;
61 
62 /**
63  * core functions of code signing
64  *
65  * @since 2023/06/05
66  */
67 public class CodeSigning {
68     /**
69      * Only hap and hsp, hqf bundle supports code signing
70      */
71     public static final String[] SUPPORT_FILE_FORM = {"hap", "hsp", "hqf"};
72 
73     /**
74      * Only elf file supports bin code signing
75      */
76     public static final String SUPPORT_BIN_FILE_FORM = "elf";
77 
78     /**
79      * Defined entry name of hap file
80      */
81     public static final String HAP_SIGNATURE_ENTRY_NAME = "Hap";
82 
83     /**
84      * bundle type of plugin
85      */
86     public static final String BUNDLE_TYPE_PLUGIN = "appPlugin";
87 
88     private static final LogUtils LOGGER = new LogUtils(CodeSigning.class);
89 
90     private final SignerConfig signConfig;
91 
92     private CodeSignBlock codeSignBlock;
93 
94     private PageInfoExtension pageInfoExtension;
95 
96     private String pluginId;
97 
98     /**
99      * provide code sign functions to sign a hap
100      *
101      * @param signConfig configuration of sign
102      */
CodeSigning(SignerConfig signConfig)103     public CodeSigning(SignerConfig signConfig) {
104         this.signConfig = signConfig;
105     }
106 
107     /**
108      * Sign the given elf file, and pack all signature into output file
109      *
110      * @param input  file to sign
111      * @param offset position of codesign block based on start of the file
112      * @param inForm file's format
113      * @param profileContent profile of the elf
114      * @return byte array of code sign block
115      * @throws CodeSignException        code signing exception
116      * @throws IOException              io error
117      * @throws FsVerityDigestException  computing FsVerity digest error
118      * @throws ProfileException         profile of elf is invalid
119      */
getElfCodeSignBlock(File input, long offset, String inForm, String profileContent)120     public byte[] getElfCodeSignBlock(File input, long offset, String inForm, String profileContent)
121         throws CodeSignException, FsVerityDigestException, IOException, ProfileException {
122         LOGGER.info("Start to sign code.");
123         if (!SUPPORT_BIN_FILE_FORM.equalsIgnoreCase(inForm)) {
124             throw new CodeSignException(CodeSignErrMsg.FILE_FORMAT_UNSUPPORTED_ERROR.toString(SUPPORT_BIN_FILE_FORM));
125         }
126         long fileSize = input.length();
127         int paddingSize = ElfSignBlock.computeMerkleTreePaddingLength(offset);
128         long fsvTreeOffset = offset + Integer.BYTES * 2 + paddingSize;
129         try (FileInputStream inputStream = new FileInputStream(input)) {
130             FsVerityGenerator fsVerityGenerator = new FsVerityGenerator();
131             fsVerityGenerator.generateFsVerityDigest(inputStream, fileSize, fsvTreeOffset);
132             byte[] fsVerityDigest = fsVerityGenerator.getFsVerityDigest();
133             // ownerID should be DEBUG_LIB_ID while signing ELF
134             String ownerID = (profileContent == null)
135                 ? HapUtils.HAP_DEBUG_OWNER_ID
136                 : HapUtils.getAppIdentifier(profileContent);
137             byte[] signature = generateSignature(fsVerityDigest, ownerID);
138             // add fs-verify info
139             FsVerityDescriptor.Builder fsdbuilder = new FsVerityDescriptor.Builder().setFileSize(fileSize)
140                 .setHashAlgorithm(FsVerityGenerator.getFsVerityHashAlgorithm())
141                 .setLog2BlockSize(FsVerityGenerator.getLog2BlockSize())
142                 .setSaltSize((byte) fsVerityGenerator.getSaltSize())
143                 .setSignSize(signature.length)
144                 .setFileSize(fileSize)
145                 .setSalt(fsVerityGenerator.getSalt())
146                 .setRawRootHash(fsVerityGenerator.getRootHash())
147                 .setFlags(FsVerityDescriptor.FLAG_STORE_MERKLE_TREE_OFFSET)
148                 .setMerkleTreeOffset(fsvTreeOffset)
149                 .setCsVersion(FsVerityDescriptor.CODE_SIGN_VERSION);
150             FsVerityDescriptorWithSign fsVerityDescriptorWithSign = new FsVerityDescriptorWithSign(fsdbuilder.build(),
151                 signature);
152             byte[] treeBytes = fsVerityGenerator.getTreeBytes();
153             ElfSignBlock signBlock = new ElfSignBlock(paddingSize, treeBytes, fsVerityDescriptorWithSign);
154             LOGGER.info("Sign elf successfully.");
155             return signBlock.toByteArray();
156         } catch (PageInfoException e) {
157             throw new CodeSignException(e.getMessage());
158         }
159     }
160 
161     /**
162      * Sign the given hap file, and pack all signature into output file
163      *
164      * @param input  file to sign
165      * @param offset position of codesign block based on start of the file
166      * @param inForm file's format
167      * @param profileContent profile of the hap
168      * @param zip zip
169      * @return byte array of code sign block
170      * @throws CodeSignException        code signing exception
171      * @throws IOException              io error
172      * @throws HapFormatException       hap format invalid
173      * @throws FsVerityDigestException  computing FsVerity digest error
174      * @throws ProfileException         profile of the hap error
175      */
getCodeSignBlock(File input, long offset, String inForm, String profileContent, Zip zip)176     public byte[] getCodeSignBlock(File input, long offset, String inForm, String profileContent, Zip zip)
177         throws CodeSignException, IOException, HapFormatException, FsVerityDigestException, ProfileException {
178         LOGGER.info("Start to sign code.");
179         if (!StringUtils.containsIgnoreCase(SUPPORT_FILE_FORM, inForm)) {
180             throw new CodeSignException(
181                 CodeSignErrMsg.FILE_FORMAT_UNSUPPORTED_ERROR.toString(String.join(",", SUPPORT_FILE_FORM)));
182         }
183         long dataSize = computeDataSize(zip);
184         // generate CodeSignBlock
185         this.codeSignBlock = new CodeSignBlock();
186         // compute merkle tree offset, replace with computeMerkleTreeOffset if fs-verity descriptor supports
187         long fsvTreeOffset = this.codeSignBlock.computeMerkleTreeOffset(offset);
188         // update fs-verity segment
189         FsVerityInfoSegment fsVerityInfoSegment = new FsVerityInfoSegment(FsVerityDescriptor.VERSION,
190             FsVerityGenerator.getFsVerityHashAlgorithm(), FsVerityGenerator.getLog2BlockSize());
191         this.codeSignBlock.setFsVerityInfoSegment(fsVerityInfoSegment);
192         String moduleContent = HapUtils.getModuleContent(input);
193         String bundleType = HapUtils.getBundleTypeFromJson(moduleContent);
194         if (BUNDLE_TYPE_PLUGIN.equals(bundleType)) {
195             pluginId = HapUtils.parsePluginId(profileContent);
196         }
197         String ownerID = HapUtils.getAppIdentifier(profileContent);
198         try (FileInputStream inputStream = new FileInputStream(input)) {
199             Pair<SignInfo, byte[]> hapSignInfoAndMerkleTreeBytesPair = signFile(inputStream, dataSize, true,
200                 fsvTreeOffset, ownerID);
201             // update hap segment in CodeSignBlock
202             this.codeSignBlock.getHapInfoSegment().setSignInfo(hapSignInfoAndMerkleTreeBytesPair.getFirst());
203             // Insert merkle tree bytes into code sign block
204             this.codeSignBlock.addOneMerkleTree(HAP_SIGNATURE_ENTRY_NAME,
205                 hapSignInfoAndMerkleTreeBytesPair.getSecond());
206         }
207         // update native lib info segment in CodeSignBlock
208         List<Pair<String, SignInfo>> nativeLibInfoList = new ArrayList<>();
209         nativeLibInfoList.addAll(signNativeLibs(input, ownerID));
210         nativeLibInfoList.addAll(signNativeHnps(input, profileContent, ownerID));
211         // update SoInfoSegment in CodeSignBlock
212         this.codeSignBlock.getSoInfoSegment().setSoInfoList(nativeLibInfoList);
213 
214         // last update codeSignBlock before generating its byte array representation
215         updateCodeSignBlock(this.codeSignBlock);
216 
217         // complete code sign block byte array here
218         byte[] generated = this.codeSignBlock.generateCodeSignBlockByte(fsvTreeOffset);
219         LOGGER.info("Sign successfully.");
220         return generated;
221     }
222 
createPageInfoExtension(ZipEntry entry)223     private void createPageInfoExtension(ZipEntry entry) {
224         long bitmapOff = entry.getCentralDirectory().getOffset() + ZipEntryHeader.HEADER_LENGTH
225             + entry.getZipEntryData().getZipEntryHeader().getFileNameLength() + entry.getZipEntryData()
226             .getZipEntryHeader()
227             .getExtraLength();
228         long bitmapSize = bitmapOff / CodeSignBlock.PAGE_SIZE_4K * PageInfoExtension.DEFAULT_UNIT_SIZE;
229         pageInfoExtension = new PageInfoExtension(bitmapOff, bitmapSize);
230     }
231 
computeDataSize(Zip zip)232     private long computeDataSize(Zip zip) throws HapFormatException {
233         long dataSize = 0L;
234         for (ZipEntry entry : zip.getZipEntries()) {
235             ZipEntryHeader zipEntryHeader = entry.getZipEntryData().getZipEntryHeader();
236             EntryType type = entry.getZipEntryData().getType();
237             short method = zipEntryHeader.getMethod();
238             if ((EntryType.RUNNABLE_FILE.equals(type) && method == Zip.FILE_UNCOMPRESS_METHOD_FLAG)) {
239                 continue;
240             }
241             if (EntryType.BIT_MAP.equals(type)) {
242                 createPageInfoExtension(entry);
243                 continue;
244             }
245             // if the first file is not uncompressed abc or so, set dataSize to zero
246             if (entry.getCentralDirectory().getOffset() == 0) {
247                 break;
248             }
249             // the first entry which is not abc/so/an is found, return its data offset
250             dataSize = entry.getCentralDirectory().getOffset() + ZipEntryHeader.HEADER_LENGTH
251                     + zipEntryHeader.getFileNameLength() + zipEntryHeader.getExtraLength();
252             break;
253         }
254         if (!NumberUtils.isMultiple4K(dataSize)) {
255             throw new HapFormatException(CodeSignErrMsg.FILE_4K_ALIGNMENT_ERROR.toString(dataSize));
256         }
257         return dataSize;
258     }
259 
signNativeLibs(File input, String ownerID)260     private List<Pair<String, SignInfo>> signNativeLibs(File input, String ownerID)
261         throws IOException, FsVerityDigestException, CodeSignException {
262         // sign native files
263         try (JarFile inputJar = new JarFile(input, false)) {
264             List<String> entryNames = getNativeEntriesFromHap(inputJar);
265             if (entryNames.isEmpty()) {
266                 LOGGER.info("No native libs.");
267                 return new ArrayList<>();
268             }
269             return signFilesFromJar(entryNames, inputJar, ownerID);
270         }
271     }
272 
signNativeHnps(File input, String profileContent, String ownerID)273     private List<Pair<String, SignInfo>> signNativeHnps(File input, String profileContent, String ownerID)
274         throws IOException, CodeSignException, ProfileException {
275         List<Pair<String, SignInfo>> nativeLibInfoList = new ArrayList<>();
276         try (JarFile inputJar = new JarFile(input, false)) {
277             Map<String, String> hnpTypeMap = HapUtils.getHnpsFromJson(inputJar);
278             // get hnp entry
279             for (Enumeration<JarEntry> e = inputJar.entries(); e.hasMoreElements(); ) {
280                 JarEntry entry = e.nextElement();
281                 String entryName = entry.getName();
282                 if (entry.isDirectory() || !entryName.startsWith("hnp/") || !entryName.toLowerCase(Locale.ROOT)
283                     .endsWith(".hnp")) {
284                     continue;
285                 }
286                 String hnpFileName = HapUtils.parseHnpPath(entryName);
287                 if (!hnpTypeMap.containsKey(hnpFileName)) {
288                     throw new CodeSignException(CodeSignErrMsg.HNP_FILE_DESCRIPTION_ERROR.toString(entryName));
289                 }
290                 LOGGER.debug("Sign hnp name = {}", entryName);
291                 String type = hnpTypeMap.get(hnpFileName);
292                 String hnpOwnerId = ownerID;
293                 if ("public".equals(type)) {
294                     hnpOwnerId = HapUtils.getPublicHnpOwnerId(profileContent);
295                 }
296                 nativeLibInfoList.addAll(signHnpLibs(inputJar, entry, hnpOwnerId));
297             }
298         }
299         return nativeLibInfoList;
300     }
301 
signHnpLibs(JarFile inputJar, JarEntry hnpEntry, String ownerID)302     private List<Pair<String, SignInfo>> signHnpLibs(JarFile inputJar, JarEntry hnpEntry, String ownerID)
303         throws IOException, CodeSignException {
304         File tempHnp = File.createTempFile("tmp-", ".hnp");
305         writeTempHnpFile(inputJar, hnpEntry, tempHnp);
306         if (!tempHnp.exists() || tempHnp.length() == 0) {
307             throw new CodeSignException(CodeSignErrMsg.EXTRACT_HNP_FILE_ERROR.toString(hnpEntry.getName()));
308         }
309         try (JarFile hnp = new JarFile(tempHnp, false)) {
310             List<JarEntry> elfEntries = getHnpLibEntries(hnp);
311             LOGGER.debug("{} elf num : {}", hnpEntry.getName(), elfEntries.size());
312             List<Pair<String, SignInfo>> nativeLibInfoList = elfEntries.stream().parallel().map(entry -> {
313                 String hnpElfPath = hnpEntry.getName() + "!/" + entry.getName();
314                 try (InputStream inputStream = hnp.getInputStream(entry)) {
315                     // We don't store merkle tree in code signing of native libs
316                     // Therefore, the second value of pair returned is ignored
317                     Pair<SignInfo, byte[]> pairSignInfoAndMerkleTreeBytes = signFile(inputStream, entry.getSize(),
318                         false, 0, ownerID);
319                     return (Pair.create(hnpElfPath, pairSignInfoAndMerkleTreeBytes.getFirst()));
320                 } catch (IOException | FsVerityDigestException | CodeSignException e) {
321                     LOGGER.error("Sign hnp lib error msg : {} AT entry : {}" + System.lineSeparator(), e.getMessage(),
322                         hnpElfPath);
323                 }
324                 return null;
325             }).collect(Collectors.toList());
326             if (nativeLibInfoList.contains(null)) {
327                 throw new CodeSignException("Sign hnp lib error");
328             }
329             return nativeLibInfoList;
330         } catch (IOException e) {
331             throw new CodeSignException(CodeSignErrMsg.EXTRACT_HNP_FILE_ERROR.toString(hnpEntry.getName()), e);
332         } finally {
333             if (tempHnp.exists()) {
334                 if (tempHnp.delete()) {
335                     LOGGER.debug("delete temp hnp file {}", tempHnp.getName());
336                 } else {
337                     LOGGER.error("delete temp hnp file error {}", tempHnp.getName());
338                 }
339             }
340         }
341     }
342 
getHnpLibEntries(JarFile hnp)343     private List<JarEntry> getHnpLibEntries(JarFile hnp) throws IOException {
344         List<JarEntry> elfEntries = new ArrayList<>();
345         for (Enumeration<JarEntry> e = hnp.entries(); e.hasMoreElements(); ) {
346             JarEntry entry = e.nextElement();
347             try (InputStream inputStream = hnp.getInputStream(entry)) {
348                 byte[] bytes = new byte[4];
349                 inputStream.read(bytes);
350                 if (ElfHeader.isElfFile(bytes)) {
351                     elfEntries.add(entry);
352                 }
353             }
354         }
355         return elfEntries;
356     }
357 
writeTempHnpFile(JarFile inputJar, JarEntry hnpEntry, File tempHnp)358     private void writeTempHnpFile(JarFile inputJar, JarEntry hnpEntry, File tempHnp) {
359         try (InputStream inputStream = inputJar.getInputStream(hnpEntry);
360             FileOutputStream fos = new FileOutputStream(tempHnp)) {
361             int read;
362             // buffered 64k
363             byte[] bytes = new byte[1024 * 64];
364             while ((read = inputStream.read(bytes)) != -1) {
365                 fos.write(bytes, 0, read);
366             }
367         } catch (IOException e) {
368             LOGGER.error("write temp hnp file error ", e);
369         }
370     }
371 
372     /**
373      * Get entry name of all native files in hap
374      *
375      * @param hap the given hap
376      * @return list of entry name
377      */
getNativeEntriesFromHap(JarFile hap)378     private List<String> getNativeEntriesFromHap(JarFile hap) {
379         List<String> result = new ArrayList<>();
380         for (Enumeration<JarEntry> e = hap.entries(); e.hasMoreElements();) {
381             JarEntry entry = e.nextElement();
382             if (!entry.isDirectory()) {
383                 if (!isNativeFile(entry.getName())) {
384                     continue;
385                 }
386                 result.add(entry.getName());
387             }
388         }
389         return result;
390     }
391 
392     /**
393      * Check whether the entry is a native file
394      *
395      * @param entryName the name of entry
396      * @return true if it is a native file, and false otherwise
397      */
isNativeFile(String entryName)398     private boolean isNativeFile(String entryName) {
399         if (StringUtils.isEmpty(entryName)) {
400             return false;
401         }
402         if (entryName.endsWith(FileUtils.NATIVE_LIB_AN_SUFFIX)) {
403             return true;
404         }
405         if (entryName.startsWith(FileUtils.LIBS_PATH_PREFIX)) {
406             return true;
407         }
408         return false;
409     }
410 
411     /**
412      * Sign specific entries in a hap
413      *
414      * @param entryNames list of entries which need to be signed
415      * @param hap        input hap
416      * @param ownerID    app-id in signature to identify
417      * @return sign info and merkle tree of each file
418      * @throws CodeSignException       sign error
419      */
signFilesFromJar(List<String> entryNames, JarFile hap, String ownerID)420     private List<Pair<String, SignInfo>> signFilesFromJar(List<String> entryNames, JarFile hap, String ownerID)
421         throws CodeSignException {
422         List<Pair<String, SignInfo>> nativeLibInfoList = entryNames.stream().parallel().map(name -> {
423             LOGGER.debug("Sign entry name = {}", name);
424             JarEntry inEntry = hap.getJarEntry(name);
425             try (InputStream inputStream = hap.getInputStream(inEntry)) {
426                 long fileSize = inEntry.getSize();
427                 // We don't store merkle tree in code signing of native libs
428                 // Therefore, the second value of pair returned is ignored
429                 Pair<SignInfo, byte[]> pairSignInfoAndMerkleTreeBytes = signFile(inputStream, fileSize, false, 0,
430                     ownerID);
431                 return Pair.create(name, pairSignInfoAndMerkleTreeBytes.getFirst());
432             } catch (FsVerityDigestException | CodeSignException | IOException e) {
433                 LOGGER.error("Sign lib error msg : {} AT entry : {}" + System.lineSeparator(), e.getMessage(), name);
434             }
435             return null;
436         }).collect(Collectors.toList());
437         if (nativeLibInfoList.contains(null)) {
438             throw new CodeSignException("Sign lib error");
439         }
440         return nativeLibInfoList;
441     }
442 
443     /**
444      * Sign a file from input stream
445      *
446      * @param inputStream   input stream of a file
447      * @param fileSize      size of the file
448      * @param storeTree     whether to store merkle tree in signed info
449      * @param fsvTreeOffset merkle tree raw bytes offset based on the start of file
450      * @param ownerID       app-id in signature to identify
451      * @return pair of signature and tree
452      * @throws FsVerityDigestException computing FsVerity Digest error
453      * @throws CodeSignException       signing error
454      */
signFile(InputStream inputStream, long fileSize, boolean storeTree, long fsvTreeOffset, String ownerID)455     public Pair<SignInfo, byte[]> signFile(InputStream inputStream, long fileSize, boolean storeTree,
456         long fsvTreeOffset, String ownerID) throws FsVerityDigestException, CodeSignException {
457         FsVerityGenerator fsVerityGenerator = new FsVerityGenerator();
458         fsVerityGenerator.setPageInfoExtension(pageInfoExtension);
459         try {
460             fsVerityGenerator.generateFsVerityDigest(inputStream, fileSize, fsvTreeOffset);
461         } catch (PageInfoException e) {
462             throw new CodeSignException(e.getMessage());
463         }
464         byte[] fsVerityDigest = fsVerityGenerator.getFsVerityDigest();
465         byte[] signature = generateSignature(fsVerityDigest, ownerID);
466         int flags = 0;
467         if (storeTree) {
468             flags = SignInfo.FLAG_MERKLE_TREE_INCLUDED;
469         }
470         SignInfo signInfo = new SignInfo(fsVerityGenerator.getSaltSize(), flags, fileSize, fsVerityGenerator.getSalt(),
471             signature);
472         // if store merkle tree in sign info
473         if (storeTree) {
474             int merkleTreeSize = fsVerityGenerator.getTreeBytes() == null ? 0 : fsVerityGenerator.getTreeBytes().length;
475             Extension merkleTreeExtension = new MerkleTreeExtension(merkleTreeSize, fsvTreeOffset,
476                 fsVerityGenerator.getRootHash());
477             signInfo.addExtension(merkleTreeExtension);
478             if (pageInfoExtension != null) {
479                 byte[] fsVerityDigestV2 = fsVerityGenerator.getFsVerityDigestV2();
480                 byte[] signatureV2 = generateSignature(fsVerityDigestV2, ownerID);
481                 pageInfoExtension.setSignature(signatureV2);
482                 signInfo.addExtension(pageInfoExtension);
483                 LOGGER.debug(pageInfoExtension.toString());
484             }
485         }
486         return Pair.create(signInfo, fsVerityGenerator.getTreeBytes());
487     }
488 
generateSignature(byte[] signedData, String ownerID)489     private byte[] generateSignature(byte[] signedData, String ownerID) throws CodeSignException {
490         SignerConfig copiedConfig = signConfig;
491         // signConfig is created by SignerFactory
492         if ((copiedConfig.getSigner() instanceof LocalSigner)) {
493             if (copiedConfig.getCertificates().isEmpty()) {
494                 throw new CodeSignException(
495                     CodeSignErrMsg.CERTIFICATES_CONFIGURE_ERROR.toString("No certificate is configured for sign"));
496             }
497             BcSignedDataGenerator bcSignedDataGenerator = new BcSignedDataGenerator();
498             bcSignedDataGenerator.setOwnerID(ownerID);
499             bcSignedDataGenerator.setPluginId(pluginId);
500             return bcSignedDataGenerator.generateSignedData(signedData, copiedConfig);
501         } else {
502             copiedConfig = signConfig.copy();
503             BcSignedDataGenerator bcSignedDataGenerator = new BcSignedDataGenerator();
504             bcSignedDataGenerator.setOwnerID(ownerID);
505             bcSignedDataGenerator.setPluginId(pluginId);
506             return bcSignedDataGenerator.generateSignedData(signedData, copiedConfig);
507         }
508     }
509 
510     /**
511      * At here, segment header, fsverity info/hap/so info segment, merkle tree
512      * segment should all be generated.
513      * code sign block size, segment number, offset is not updated.
514      * Try to update whatever could be updated here.
515      *
516      * @param codeSignBlock CodeSignBlock
517      */
updateCodeSignBlock(CodeSignBlock codeSignBlock)518     private void updateCodeSignBlock(CodeSignBlock codeSignBlock) {
519         // construct segment header list
520         codeSignBlock.setSegmentHeaders();
521         // Compute and set segment number
522         codeSignBlock.setSegmentNum();
523         // update code sign block header flag
524         codeSignBlock.setCodeSignBlockFlag();
525         // compute segment offset
526         codeSignBlock.computeSegmentOffset();
527     }
528 
529 }
530