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 #include <unistd.h>
16 
17 #include "sign_elf.h"
18 #include "string_utils.h"
19 #include "code_signing.h"
20 #include "param_constants.h"
21 #include "block_head.h"
22 #include "sign_head.h"
23 #include "signature_block_types.h"
24 #include "signature_block_tags.h"
25 
26 namespace OHOS {
27 namespace SignatureTools {
28 
29 int SignElf::blockNum = 0;
30 const std::string SignElf::CODESIGN_OFF = "0";
31 
Sign(SignerConfig& signerConfig, std::map<std::string, std::string>& signParams)32 bool SignElf::Sign(SignerConfig& signerConfig, std::map<std::string, std::string>& signParams)
33 {
34     std::string inputFile = signParams.at(ParamConstants::PARAM_BASIC_INPUT_FILE);
35     std::string tmpFile;
36     bool checkAlignFileBy4kBytesFlag = AlignFileBy4kBytes(inputFile, tmpFile);
37     if (!checkAlignFileBy4kBytesFlag) {
38         SIGNATURE_TOOLS_LOGE("[SignElf] AlignFileBy4kBytes error");
39         remove(tmpFile.c_str());
40         return false;
41     }
42     std::string outputFile = signParams.at(ParamConstants::PARAM_BASIC_OUTPUT_FILE);
43     std::string profileSigned = signParams.at(ParamConstants::PARAM_BASIC_PROFILE_SIGNED);
44     bool checkWriteBlockDataToFileFlag = WriteBlockDataToFile(signerConfig, tmpFile,
45                                                               outputFile, profileSigned, signParams);
46     if (!checkWriteBlockDataToFileFlag) {
47         SIGNATURE_TOOLS_LOGE("[SignElf] WriteBlockDataToFile error");
48         remove(tmpFile.c_str());
49         return false;
50     }
51     bool checkWriteSignHeadDataToOutputFileFlag = WriteSignHeadDataToOutputFile(tmpFile, outputFile, blockNum);
52     if (!checkWriteSignHeadDataToOutputFileFlag) {
53         SIGNATURE_TOOLS_LOGE("[SignElf] WriteSignHeadDataToOutputFile error");
54         remove(tmpFile.c_str());
55         return false;
56     }
57     return (remove(tmpFile.c_str()) == 0);
58     ;
59 }
60 
AlignFileBy4kBytes(const std::string& inputFile, std::string& tmpFile)61 bool SignElf::AlignFileBy4kBytes(const std::string& inputFile, std::string& tmpFile)
62 {
63     auto now = std::chrono::high_resolution_clock::now();
64     auto duration = now.time_since_epoch();
65     auto timeStamp = std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
66     tmpFile = "tmpFile" + std::to_string(timeStamp);
67     std::ofstream output(tmpFile);
68     bool checkSpaceFlag = FileUtils::IsSpaceEnough(std::string("./"), FileUtils::GetFileLen(inputFile));
69     if (!checkSpaceFlag) {
70         char currentPath[FILE_PATH_LENGTH] = { 0 };
71         getcwd(currentPath, FILE_PATH_LENGTH);
72         PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "[SignElf] The available space of the current directory: "
73                             + std::string(currentPath) + " is insufficient. Please check");
74         return false;
75     }
76     bool checkOutputFlag = output.is_open();
77     if (!checkOutputFlag) {
78         PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "[SignElf] open file: " + tmpFile + "failed");
79         return false;
80     }
81     std::ifstream input(inputFile);
82     bool checkInputFlag = input.is_open();
83     if (!checkInputFlag) {
84         PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "[SignElf] open file: " + inputFile + "failed");
85         return false;
86     }
87     char buffer[FILE_BUFFER_BLOCK];
88     std::streamsize bytesRead;
89     int64_t outputLength = 0;
90     while ((bytesRead = input.read(buffer, sizeof(buffer)).gcount()) > 0) {
91         output.write(buffer, bytesRead);
92         if (!output) {
93             PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "[SignElf] write data to " + tmpFile + "failed");
94             return false;
95         }
96         outputLength += bytesRead;
97     }
98     int64_t addLength = PAGE_SIZE - (outputLength % PAGE_SIZE);
99     std::vector<char> bytes(addLength, 0);
100     output.write(bytes.data(), addLength);
101     return true;
102 }
103 
WriteBlockDataToFile(SignerConfig& signerConfig, const std::string &inputFile, std::string& outputFile, const std::string& profileSigned, const std::map<std::string, std::string>& signParams)104 bool SignElf::WriteBlockDataToFile(SignerConfig& signerConfig,
105                                    const std::string &inputFile, std::string& outputFile,
106                                    const std::string& profileSigned,
107                                    const std::map<std::string, std::string>& signParams)
108 {
109     std::string proFile;
110     if (signParams.find(ParamConstants::PARAM_BASIC_PROFILE) != signParams.end()) {
111         proFile = signParams.at(ParamConstants::PARAM_BASIC_PROFILE);
112     }
113     std::list<SignBlockData> signDataList;
114     int64_t binFileLen = FileUtils::GetFileLen(inputFile);
115     bool checkFlag = binFileLen < 0 || IsLongOverflowInteger(binFileLen);
116     if (checkFlag) {
117         PrintErrorNumberMsg("SIGN_ERROR", SIGN_ERROR, "[SignElf] The length exceeds the maximum limit.");
118         return false;
119     }
120     bool checkIsEmptyFlag = StringUtils::IsEmpty(proFile);
121     if (!checkIsEmptyFlag) {
122         signDataList.push_front(GenerateProfileSignByte(proFile, profileSigned));
123     }
124     blockNum = signDataList.size() + 1;
125     SignBlockData* codeSign = nullptr;
126     bool checkGenerateCodeSignByteFlag = !GenerateCodeSignByte(signerConfig, signParams, inputFile, blockNum,
127                                                                binFileLen, &codeSign) || !codeSign;
128     if (checkGenerateCodeSignByteFlag) {
129         SIGNATURE_TOOLS_LOGE("[SignElf] generate code sign byte error.");
130         if (codeSign) {
131             delete codeSign;
132         }
133         return false;
134     }
135     signDataList.push_front(*codeSign);
136     blockNum = signDataList.size();
137     bool checkGenerateSignBlockHeadFlag = GenerateSignBlockHead(signDataList);
138     if (!checkGenerateSignBlockHeadFlag) {
139         SIGNATURE_TOOLS_LOGE("[SignElf]  generate sign block head error.");
140         delete codeSign;
141         return false;
142     }
143     delete codeSign;
144     return WriteSignedElf(inputFile, signDataList, outputFile);
145 }
146 
147 bool SignElf::WriteSignedElf(const std::string &inputFile, std::list<SignBlockData>& signBlockList,
148                              std::string &outputFile)
149 {
150     std::ifstream fileInputStream(inputFile, std::ios::binary);
151     std::ofstream fileOutputStream(outputFile, std::ios::binary);
152     bool checkFlag = !fileInputStream.is_open();
153     if (checkFlag) {
154         PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "[SignElf] open file: " + inputFile + "failed");
155         return false;
156     }
157     checkFlag = !fileOutputStream.is_open();
158     if (checkFlag) {
159         fileInputStream.close();
160         PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "[SignElf] open file: " + outputFile + "failed");
161         return false;
162     }
163     char buffer[FILE_BUFFER_BLOCK];
164     while (!fileInputStream.eof()) {
165         fileInputStream.read(buffer, sizeof(buffer));
166         fileOutputStream.write(buffer, fileInputStream.gcount());
167     }
168     bool writeFlag = WriteSignBlockData(signBlockList, fileOutputStream);
169     if (!writeFlag) {
170         SIGNATURE_TOOLS_LOGE("[SignElf] write signBlockList to file error");
171         fileInputStream.close();
172         fileOutputStream.close();
173         return false;
174     }
175     fileInputStream.close();
176     fileOutputStream.close();
177     return true;
178 }
179 
180 bool SignElf::WriteSignBlockData(std::list<SignBlockData>& signBlockList, std::ofstream& fileOutputStream)
181 {
182     for (auto& signBlockData : signBlockList) {
183         bool checkWriteByteToOutFileFlag = FileUtils::WriteByteToOutFile(signBlockData.GetBlockHead(),
184                                                                          fileOutputStream);
185         if (!checkWriteByteToOutFileFlag) {
186             SIGNATURE_TOOLS_LOGE("[SignElf] write data to file error");
187             return false;
188         }
189     }
190     for (auto& signBlockData : signBlockList) {
191         bool isSuccess;
192         bool checkFlag = signBlockData.GetByte();
193         if (checkFlag) {
194             isSuccess = FileUtils::WriteByteToOutFile(signBlockData.GetSignData(), fileOutputStream);
195         } else {
196             std::ifstream InputSignFileStream(signBlockData.GetSignFile(), std::ios::binary);
197             bool checkFileFlag = !InputSignFileStream.is_open();
198             if (checkFileFlag) {
199                 PrintErrorNumberMsg("IO_ERROR", IO_ERROR,
200                                     "[SignElf] open file: " + signBlockData.GetSignFile() + "failed");
201                 return false;
202             }
203             int result = FileUtils::WriteInputToOutPut(InputSignFileStream, fileOutputStream,
204                                                        (long)signBlockData.GetLen());
205             isSuccess = (result == 0 ? true : false);
206         }
207         if (!isSuccess) {
208             PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "write data to file error");
209             return false;
210         }
211     }
212     return true;
213 }
214 
215 bool SignElf::GenerateSignBlockHead(std::list<SignBlockData>& signDataList)
216 {
217     int64_t offset = BlockHead::GetElfBlockLen() * signDataList.size();
218     for (std::list<SignBlockData>::iterator it = signDataList.begin(); it != signDataList.end(); ++it) {
219         std::vector<int8_t> tmp = BlockHead::GetBlockHeadLittleEndian(it->GetType(),
220             SignatureBlockTags::DEFAULT, it->GetLen(), offset);
221         it->SetBlockHead(tmp);
222         offset += it->GetLen();
223         bool checkIsLongOverflowIntegerFlag = IsLongOverflowInteger(offset);
224         if (checkIsLongOverflowIntegerFlag) {
225             PrintErrorNumberMsg("SIGN_ERROR", SIGN_ERROR, "[SignElf] The length exceeds the maximum limit.");
226             return false;
227         }
228     }
229     return true;
230 }
231 
GenerateProfileSignByte(std::string profileFile, std::string profileSigned)232 SignBlockData SignElf::GenerateProfileSignByte(std::string profileFile, std::string profileSigned)
233 {
234     int64_t profileDataLen = FileUtils::GetFileLen(profileFile);
235     if (profileDataLen < 0 || IsLongOverflowShort(profileDataLen)) {
236         PrintErrorNumberMsg("SIGN_ERROR", SIGN_ERROR,
237                             "[SignElf] The length exceeds the maximum limit.");
238     }
239     char isSigned = SignatureBlockTypes::GetProfileBlockTypes(profileSigned);
240     return SignBlockData(profileFile, isSigned);
241 }
242 
GenerateCodeSignByte(SignerConfig& signerConfig, const std::map<std::string, std::string> &signParams, const std::string &inputFile, const int blockNum, const long binFileLen, SignBlockData** codeSign)243 bool SignElf::GenerateCodeSignByte(SignerConfig& signerConfig, const std::map<std::string, std::string> &signParams,
244                                    const std::string &inputFile, const int blockNum, const long binFileLen,
245                                    SignBlockData** codeSign)
246 {
247     if (signParams.at(ParamConstants::PARAM_SIGN_CODE) == CODESIGN_OFF) {
248         PrintErrorNumberMsg("SIGN_ERROR", SIGN_ERROR, "[SignElf] check pamams signCode = 0 error.");
249         return false;
250     }
251     CodeSigning codeSigning(&signerConfig);
252     long offset = binFileLen + (long)BlockHead::GetElfBlockLen() * blockNum;
253     std::string profileContent;
254     if (signParams.find(ParamConstants::PARAM_PROFILE_JSON_CONTENT) != signParams.end()) {
255         profileContent = signParams.at(ParamConstants::PARAM_PROFILE_JSON_CONTENT);
256     }
257     std::vector<int8_t> codesignData;
258     bool checkGetElfCodeSignBlockFlag = codeSigning.GetElfCodeSignBlock(inputFile, offset,
259                                                                         signParams.at(ParamConstants::PARAM_IN_FORM),
260                                                                         profileContent, codesignData);
261     if (!checkGetElfCodeSignBlockFlag) {
262         SIGNATURE_TOOLS_LOGE("[SignElf] get elf code sign block error.");
263         return false;
264     }
265     *codeSign = new SignBlockData(codesignData, CODESIGN_BLOCK_TYPE);
266     return true;
267 }
268 
WriteSignHeadDataToOutputFile(const std::string& inputFile, const std::string& outputFile, const int blockNum)269 bool SignElf::WriteSignHeadDataToOutputFile(const std::string& inputFile, const std::string& outputFile,
270                                             const int blockNum)
271 {
272     int64_t size = FileUtils::GetFileLen(outputFile) - FileUtils::GetFileLen(inputFile);
273     if (IsLongOverflowInteger(size)) {
274         PrintErrorNumberMsg("SIGN_ERROR", SIGN_ERROR,
275                             "[SignElf] The length exceeds the maximum limit.");
276         return false;
277     }
278     SignHead signHeadData;
279     std::vector<int8_t> signHeadByte = signHeadData.GetSignHeadLittleEndian((int)size, blockNum);
280     std::ofstream fileOutputStream(outputFile, std::ios::app | std::ios::binary);
281     return FileUtils::WriteByteToOutFile(signHeadByte, fileOutputStream);
282 }
283 
IsLongOverflowInteger(const int64_t num)284 bool SignElf::IsLongOverflowInteger(const int64_t num)
285 {
286     return (num - (num & 0xffffffffL)) != 0;
287 }
288 
IsLongOverflowShort(const int64_t num)289 bool SignElf::IsLongOverflowShort(const int64_t num)
290 {
291     return (num - (num & 0xffffL)) != 0;
292 }
293 
294 } // namespace SignatureTools
295 } // namespace OHOS