• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024-2024 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.hapsigntoolcmd;
17 
18 import com.ohos.entity.InForm;
19 import com.ohos.entity.Mode;
20 import com.ohos.entity.ProFileSigned;
21 import com.ohos.entity.SignAppParameters;
22 import com.ohos.entity.SignCode;
23 import com.ohos.entity.SignProfileParameters;
24 import com.ohos.entity.VerifyAppParameters;
25 import com.ohos.entity.VerifyProfileParameters;
26 import com.ohos.hapsigntool.HapSignTool;
27 import com.ohos.hapsigntool.error.ERROR;
28 import com.ohos.hapsigntool.utils.FileUtils;
29 
30 import com.ohos.hapsigntool.utils.LogUtils;
31 import org.junit.jupiter.api.AfterAll;
32 import org.junit.jupiter.api.Assertions;
33 import org.junit.jupiter.api.BeforeAll;
34 import org.junit.jupiter.api.RepeatedTest;
35 
36 import java.io.File;
37 import java.io.FileOutputStream;
38 import java.io.IOException;
39 import java.util.ArrayList;
40 import java.util.List;
41 import java.util.Random;
42 import java.util.concurrent.ArrayBlockingQueue;
43 import java.util.concurrent.Callable;
44 import java.util.concurrent.CountDownLatch;
45 import java.util.concurrent.ExecutionException;
46 import java.util.concurrent.Future;
47 import java.util.concurrent.ThreadPoolExecutor;
48 import java.util.concurrent.TimeUnit;
49 import java.util.zip.ZipEntry;
50 import java.util.zip.ZipOutputStream;
51 
52 /**
53  * Concurrency Test
54  *
55  * @since 2024/03/18
56  */
57 public class ConcurrencyTest {
58     private static final String TMP_UNSIGNED_FILE = "unsigned-";
59 
60     private static final String TMP_SIGNED_FILE = "signed-";
61 
62     private static final String TMP_HAP_SUFFIX = ".hap";
63 
64     private static final String TMP_PROFILE_SUFFIX = ".p7b";
65 
66     private static final int CONCURRENT_TASK_COUNT = 100;
67 
68     private static final int LOOP_COUNT = 5;
69 
70     private static final int KEEP_ALIVE_TIMES = 30;
71 
72     private static final int MAX_ENTRY_SIZE = 10 * 1024 * 1024;
73 
74     private static final int MIN_ENTRY_SIZE = 1024 * 1024;
75 
76     private static final File TMP_DIR = new File("concurrentTest");
77 
78     private static final List<Cleanable> tmpSource = new ArrayList<>();
79     private static final LogUtils LOG = new LogUtils(ConcurrencyTest.class);
80 
81     /**
82      * before test
83      */
84     @BeforeAll
before()85     public static void before() {
86         TMP_DIR.mkdirs();
87     }
88 
89     /**
90      * after test
91      */
92     @AfterAll
after()93     public static void after() {
94         for (Cleanable c : tmpSource) {
95             c.clean();
96         }
97     }
98 
99     /**
100      * test Sign Hap
101      *
102      * @throws IOException IOException
103      * @throws ExecutionException ExecutionException
104      * @throws InterruptedException InterruptedException
105      */
106     @RepeatedTest(2)
testSignHapConcurrent()107     public void testSignHapConcurrent() throws IOException, ExecutionException, InterruptedException {
108         executeConcurrentTask();
109     }
110 
generateSignHapTask(CountDownLatch countDownLatch)111     private Callable<Boolean> generateSignHapTask(CountDownLatch countDownLatch) throws IOException {
112         File inputFile = File.createTempFile(TMP_UNSIGNED_FILE, TMP_HAP_SUFFIX, TMP_DIR);
113         File outputFile = File.createTempFile(TMP_SIGNED_FILE, TMP_HAP_SUFFIX, TMP_DIR);
114         File profile = File.createTempFile(TMP_SIGNED_FILE, TMP_PROFILE_SUFFIX, TMP_DIR);
115         tmpSource.add(new Cleanable(inputFile));
116         tmpSource.add(new Cleanable(outputFile));
117         tmpSource.add(new Cleanable(profile));
118         generateHap(inputFile);
119         return new MultiSignHapTask(inputFile, outputFile, profile, countDownLatch);
120     }
121 
generateHap(File file)122     private void generateHap(File file) throws IOException {
123         try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file))) {
124             ZipEntry zipEntry = new ZipEntry("test.so");
125             zipEntry.setMethod(ZipEntry.DEFLATED);
126             byte[] bytes = generateChunkBytes();
127             out.putNextEntry(zipEntry);
128             out.write(bytes);
129             out.closeEntry();
130         }
131     }
132 
generateChunkBytes()133     private byte[] generateChunkBytes() {
134         Random random = new Random();
135         int size = Math.max(MIN_ENTRY_SIZE, random.nextInt(MAX_ENTRY_SIZE));
136         byte[] bytes = new byte[size];
137         random.nextBytes(bytes);
138         return bytes;
139     }
140 
executeConcurrentTask()141     private void executeConcurrentTask() throws IOException, ExecutionException, InterruptedException {
142         CountDownLatch countDownLatch = new CountDownLatch(CONCURRENT_TASK_COUNT);
143         ThreadPoolExecutor executor = new ThreadPoolExecutor(CONCURRENT_TASK_COUNT, CONCURRENT_TASK_COUNT,
144                 KEEP_ALIVE_TIMES, TimeUnit.SECONDS, new ArrayBlockingQueue<>(CONCURRENT_TASK_COUNT),
145                 new ThreadPoolExecutor.DiscardPolicy());
146         List<Future<Boolean>> futures = new ArrayList<>(CONCURRENT_TASK_COUNT);
147         for (int i = 0; i < CONCURRENT_TASK_COUNT; i++) {
148             futures.add(executor.submit(generateSignHapTask(countDownLatch)));
149         }
150         executor.shutdown();
151         boolean isFinished;
152         try {
153             isFinished = countDownLatch.await(KEEP_ALIVE_TIMES, TimeUnit.SECONDS);
154         } catch (InterruptedException e) {
155             isFinished = false;
156             LOG.error("concurrency test interrupted", e);
157         }
158         if (!isFinished) {
159             executor.shutdownNow();
160         }
161         Assertions.assertTrue(isFinished, "task time out : " + KEEP_ALIVE_TIMES + " seconds");
162         for (Future<Boolean> f : futures) {
163             Boolean isSuccess = f.get();
164             Assertions.assertNotNull(isSuccess, "task not finished");
165             Assertions.assertTrue(isSuccess, "task failed");
166         }
167     }
168 
169     private static class Cleanable {
170         private final File file;
171 
Cleanable(File file)172         public Cleanable(File file) {
173             this.file = file;
174         }
175 
clean()176         private void clean() {
177             FileUtils.deleteFile(file);
178         }
179     }
180 
181     private static class MultiSignHapTask implements Callable<Boolean> {
182         private final File inputFile;
183 
184         private final File outputFile;
185 
186         private final File profile;
187 
188         private final CountDownLatch countDownLatch;
189 
MultiSignHapTask(File inputFile, File outputFile, File profile, CountDownLatch countDownLatch)190         private MultiSignHapTask(File inputFile, File outputFile, File profile, CountDownLatch countDownLatch) {
191             this.inputFile = inputFile;
192             this.outputFile = outputFile;
193             this.profile = profile;
194             this.countDownLatch = countDownLatch;
195         }
196 
197         @Override
call()198         public Boolean call() throws IOException {
199             try {
200                 for (int i = 0; i < LOOP_COUNT; i++) {
201                     if (!signProfile()) {
202                         return false;
203                     }
204 
205                     if (!verifyProfile()) {
206                         return false;
207                     }
208 
209                     if (!signApp()) {
210                         return false;
211                     }
212                     if (!verifyApp()) {
213                         return false;
214                     }
215                 }
216                 return true;
217             } finally {
218                 countDownLatch.countDown();
219             }
220         }
221 
signProfile()222         private boolean signProfile() throws IOException {
223             SignProfileParameters signProfileParameters = new SignProfileParameters();
224             signProfileParameters.setMode(Mode.LOCAL_SIGN);
225             signProfileParameters.setKeyAlias("oh-app1-key-v1");
226             signProfileParameters.setKeyPwd("123456".toCharArray());
227             signProfileParameters.setProfileCertFile("../../tools/app1.pem");
228             signProfileParameters.setInFile("../../tools/profile.json");
229             signProfileParameters.setSignAlg("SHA256withECDSA");
230             signProfileParameters.setKeyStoreFile("../../tools/ohtest_pass.jks");
231             signProfileParameters.setKeystorePwd("123456".toCharArray());
232             signProfileParameters.setOutFile(profile.getCanonicalPath());
233             return HapSignTool.signProfile(signProfileParameters).getErrCode() == ERROR.SUCCESS_CODE;
234         }
235 
verifyProfile()236         private boolean verifyProfile() throws IOException {
237             VerifyProfileParameters verifyProfileParameters = new VerifyProfileParameters();
238             verifyProfileParameters.setInFile(profile.getCanonicalPath());
239             verifyProfileParameters.setOutFile("out.json");
240             return HapSignTool.verifyProfile(verifyProfileParameters).getErrCode() == ERROR.SUCCESS_CODE;
241         }
242 
signApp()243         private boolean signApp() throws IOException {
244             SignAppParameters signAppParameters = new SignAppParameters();
245             signAppParameters.setMode(Mode.LOCAL_SIGN);
246             signAppParameters.setKeyAlias("oh-app1-key-v1");
247             signAppParameters.setKeyPwd("123456".toCharArray());
248             signAppParameters.setAppCertFile("../../tools/app1.pem");
249             signAppParameters.setProfileFile(profile.getCanonicalPath());
250             signAppParameters.setInFile(inputFile.getCanonicalPath());
251             signAppParameters.setSignAlg("SHA256withECDSA");
252             signAppParameters.setKeyStoreFile("../../tools/ohtest_pass.jks");
253             signAppParameters.setKeystorePwd("123456".toCharArray());
254             signAppParameters.setOutFile(outputFile.getCanonicalPath());
255             signAppParameters.setProfileSigned(ProFileSigned.SIGNED);
256             signAppParameters.setInForm(InForm.ZIP);
257             signAppParameters.setSignCode(SignCode.OPEN);
258             return HapSignTool.signApp(signAppParameters).getErrCode() == ERROR.SUCCESS_CODE;
259         }
260 
verifyApp()261         private boolean verifyApp() throws IOException {
262             VerifyAppParameters verifyAppParameters = new VerifyAppParameters();
263             verifyAppParameters.setInFile(outputFile.getCanonicalPath());
264             verifyAppParameters.setOutCertChain("out.cer");
265             verifyAppParameters.setOutProfile("out.p7b");
266             return HapSignTool.verifyApp(verifyAppParameters).getErrCode() == ERROR.SUCCESS_CODE;
267         }
268     }
269 }
270