2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 * @file SignatureValidator.cpp
18 * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
20 * @brief Implementatin of tizen signature validation protocol.
23 #include <vcore/SignatureValidator.h>
24 #include <vcore/CertificateCollection.h>
25 #include <vcore/Certificate.h>
26 #include <vcore/ReferenceValidator.h>
27 #include <vcore/ValidatorFactories.h>
28 #include <vcore/XmlsecAdapter.h>
29 #include <vcore/SignatureReader.h>
30 #include <vcore/SignatureFinder.h>
32 #include <dpl/log/log.h>
36 const std::string TOKEN_ROLE_AUTHOR_URI =
37 "http://www.w3.org/ns/widgets-digsig#role-author";
38 const std::string TOKEN_ROLE_DISTRIBUTOR_URI =
39 "http://www.w3.org/ns/widgets-digsig#role-distributor";
40 const std::string TOKEN_PROFILE_URI =
41 "http://www.w3.org/ns/widgets-digsig#profile";
43 static tm _ASN1_GetTimeT(ASN1_TIME *time)
46 const char* str = (const char *)time->data;
49 memset(&t, 0, sizeof(t));
51 if (time->type == V_ASN1_UTCTIME) {
53 t.tm_year = (str[i] - '0') * 10 + (str[i + 1] - '0');
57 } else if (time->type == V_ASN1_GENERALIZEDTIME) {
61 + (str[i + 1] - '0') * 100
62 + (str[i + 2] - '0') * 10
68 t.tm_mon = (str[i] - '0') * 10 + (str[i + 1] - '0') - 1; // -1 since January is 0 not 1.
69 t.tm_mday = (str[i + 2] - '0') * 10 + (str[i + 3] - '0');
70 t.tm_hour = (str[i + 4] - '0') * 10 + (str[i + 5] - '0');
71 t.tm_min = (str[i + 6] - '0') * 10 + (str[i + 7] - '0');
72 t.tm_sec = (str[i + 8] - '0') * 10 + (str[i + 9] - '0');
74 /* Note: we did not adjust the time based on time zone information */
78 static bool checkRoleURI(const ValidationCore::SignatureData &data)
80 std::string roleURI = data.getRoleURI();
82 if (roleURI.empty()) {
83 LogWarning("URI attribute in Role tag couldn't be empty.");
87 if (roleURI != TOKEN_ROLE_AUTHOR_URI && data.isAuthorSignature()) {
88 LogWarning("URI attribute in Role tag does not "
89 "match with signature filename.");
93 if (roleURI != TOKEN_ROLE_DISTRIBUTOR_URI && !data.isAuthorSignature()) {
94 LogWarning("URI attribute in Role tag does not "
95 "match with signature filename.");
101 static bool checkProfileURI(const ValidationCore::SignatureData &data)
103 if (TOKEN_PROFILE_URI != data.getProfileURI()) {
104 LogWarning("Profile tag contains unsupported value "
105 "in URI attribute " << data.getProfileURI());
111 static bool checkObjectReferences(const ValidationCore::SignatureData &data)
113 ValidationCore::ObjectList objectList = data.getObjectList();
114 ValidationCore::ObjectList::const_iterator iter;
115 for (iter = objectList.begin(); iter != objectList.end(); ++iter) {
116 if (!data.containObjectReference(*iter)) {
117 LogWarning("Signature does not contain reference for object " << *iter);
124 static struct tm getMidTime(const struct tm &tb, const struct tm &ta)
127 memset(&tMid, 0, sizeof(tMid));
129 LogDebug("Certificate's notBeforeTime : Year["
130 << (tb.tm_year + 1900)
131 << "] Month[" << (tb.tm_mon + 1)
132 << "] Day[" << tb.tm_mday << "] ");
134 LogDebug("Certificate's notAfterTime : Year["
135 << (ta.tm_year + 1900)
136 << "] Month[" << (ta.tm_mon + 1)
137 << "] Day[" << ta.tm_mday << "] ");
139 int year = (ta.tm_year - tb.tm_year) / 4;
142 tMid.tm_year = tb.tm_year;
143 tMid.tm_mon = tb.tm_mon + 1;
144 tMid.tm_mday = tb.tm_mday;
146 if (tMid.tm_mon == 12) {
147 tMid.tm_year = ta.tm_year;
148 tMid.tm_mon = ta.tm_mon - 1;
149 tMid.tm_mday = ta.tm_mday;
151 if (tMid.tm_mon < 0) {
152 tMid.tm_year = ta.tm_year;
153 tMid.tm_mon = ta.tm_mon;
154 tMid.tm_mday = ta.tm_mday - 1;
156 if (tMid.tm_mday == 0) {
157 tMid.tm_year = tb.tm_year;
158 tMid.tm_mon = tb.tm_mon;
159 tMid.tm_mday = tb.tm_mday + 1;
164 tMid.tm_year = tb.tm_year + year;
165 tMid.tm_mon = (tb.tm_mon + ta.tm_mon) / 2;
166 tMid.tm_mday = (tb.tm_mday + ta.tm_mday) / 2;
169 LogDebug("cmp cert with validation time. Year["
170 << (tMid.tm_year + 1900)
171 << "] Month[" << (tMid.tm_mon + 1)
172 << "] Day[" << tMid.tm_mday << "] ");
177 } // namespace anonymouse
181 namespace ValidationCore {
184 * Prepare to check / checklist. parse xml and save info to signature data.
186 * [in] fileInfo : signature file information to check. file path should be absolute path
187 * which is made by SignatureFinder.
188 * [out] outData : signature data for validating and will be finally returned to client.
190 int prepareToCheck(const SignatureFileInfo &fileInfo, SignatureData &outData)
192 outData = SignatureData(fileInfo.getFileName(), fileInfo.getFileNumber());
196 xml.initialize(outData, SIGNATURE_SCHEMA_PATH);
199 LogError("Failed to parse signature file by signature reader.");
207 * Same logic (check, checkList) is functionalized here.
209 * [in] checkOcsp : If on, check ocsp.
210 * [out] disregard : distributor signature disregard flag.
211 * [out] context : xml sec for validating.
212 * [out] data : signature data for validationg and will be finally returned to client.
214 static SignatureValidator::Result checkInternal(
217 XmlSec::XmlSecContext &context,
220 // TODO: impl ocsp check
223 if (!checkRoleURI(data) || !checkProfileURI(data))
224 return SignatureValidator::SIGNATURE_INVALID;
226 CertificateCollection collection;
227 collection.load(data.getCertList());
229 if (!collection.sort() || collection.empty() || !collection.completeCertificateChain()) {
230 LogWarning("Certificates do not form valid chain.");
231 return SignatureValidator::SIGNATURE_INVALID;
234 CertificateList sortedCertificateList = collection.getChain();
235 CertificatePtr root = sortedCertificateList.back();
237 // Is Root CA certificate trusted?
238 CertStoreId::Set storeIdSet = createCertificateIdentifier().find(root);
240 LogDebug("root certificate from " << storeIdSet.typeToString() << " domain");
241 if (data.isAuthorSignature()) {
242 if (!storeIdSet.contains(CertStoreId::TIZEN_DEVELOPER)) {
243 LogWarning("author-signature.xml has got unrecognized Root CA "
244 "certificate. Signature will be disregarded.");
248 LogDebug("signaturefile name = " << data.getSignatureFileName());
249 if (storeIdSet.contains(CertStoreId::TIZEN_DEVELOPER)) {
250 LogError("distributor has author level siganture! Signature will be disregarded.");
251 return SignatureValidator::SIGNATURE_INVALID;
254 if (data.getSignatureNumber() == 1 && !storeIdSet.isContainsVis()) {
255 LogWarning("signature1.xml has got unrecognized Root CA "
256 "certificate. Signature will be disregarded.");
261 data.setStorageType(storeIdSet);
262 data.setSortedCertificateList(sortedCertificateList);
265 * We add only Root CA certificate because the rest
266 * of certificates are present in signature files ;-)
268 context.signatureFile = data.getSignatureFileName();
269 context.certificatePtr = root;
271 /* certificate time check */
272 ASN1_TIME* notAfterTime = data.getEndEntityCertificatePtr()->getNotAfterTime();
273 ASN1_TIME* notBeforeTime = data.getEndEntityCertificatePtr()->getNotBeforeTime();
275 time_t nowTime = time(NULL);
277 if (X509_cmp_time(notBeforeTime, &nowTime) > 0 || X509_cmp_time(notAfterTime, &nowTime) < 0) {
278 if (storeIdSet.contains(CertStoreId::TIZEN_TEST) || storeIdSet.contains(CertStoreId::TIZEN_VERIFY)) {
279 LogError("TIZEN_VERIFY : check certificate Time : FALSE");
280 return SignatureValidator::SIGNATURE_INVALID;
283 struct tm tMid = getMidTime(_ASN1_GetTimeT(notBeforeTime), _ASN1_GetTimeT(notAfterTime));
285 context.validationTime = mktime(&tMid);
288 return SignatureValidator::SIGNATURE_VERIFIED;
291 SignatureValidator::Result SignatureValidator::check(
292 const SignatureFileInfo &fileInfo,
293 const std::string &widgetContentPath,
295 bool checkReferences,
296 SignatureData &outData)
298 if (prepareToCheck(fileInfo, outData)) {
299 LogError("Failed to prepare to check.");
300 return SIGNATURE_INVALID;
303 bool disregard = false;
306 XmlSec::XmlSecContext context;
307 Result result = checkInternal(checkOcsp, disregard, context, outData);
308 if (result != SIGNATURE_VERIFIED)
311 if (!outData.isAuthorSignature()) {
312 if (XmlSec::NO_ERROR != XmlSecSingleton::Instance().validate(&context)) {
313 LogWarning("Installation break - invalid package!");
314 return SIGNATURE_INVALID;
317 outData.setReference(context.referenceSet);
318 if (!checkObjectReferences(outData)) {
319 LogWarning("Failed to check Object References");
320 return SIGNATURE_INVALID;
323 if (checkReferences) {
324 ReferenceValidator fileValidator(widgetContentPath);
325 if (ReferenceValidator::NO_ERROR != fileValidator.checkReferences(outData)) {
326 LogWarning("Invalid package - file references broken");
327 return SIGNATURE_INVALID;
331 } catch (const CertificateCollection::Exception::Base &e) {
332 LogError("CertificateCollection exception : " << e.DumpToString());
333 return SIGNATURE_INVALID;
334 } catch (const XmlSec::Exception::Base &e) {
335 LogError("XmlSec exception : " << e.DumpToString());
336 return SIGNATURE_INVALID;
338 LogError("Unknown exception in SignatureValidator::check");
339 return SIGNATURE_INVALID;
342 return disregard ? SIGNATURE_DISREGARD : SIGNATURE_VERIFIED;
345 SignatureValidator::Result SignatureValidator::checkList(
346 const SignatureFileInfo &fileInfo,
347 const std::string &widgetContentPath,
348 const std::list<std::string> &uriList,
350 bool checkReferences,
351 SignatureData &outData)
353 if (prepareToCheck(fileInfo, outData)) {
354 LogError("Failed to prepare to check.");
355 return SIGNATURE_INVALID;
358 bool disregard = false;
360 XmlSec::XmlSecContext context;
361 Result result = checkInternal(checkOcsp, disregard, context, outData);
362 if (result != SIGNATURE_VERIFIED)
365 if (uriList.size() == 0) {
366 if (XmlSec::NO_ERROR != XmlSecSingleton::Instance().validateNoHash(&context)) {
367 LogWarning("Installation break - invalid package! >> validateNoHash");
368 return SIGNATURE_INVALID;
371 XmlSecSingleton::Instance().setPartialHashList(uriList);
372 if (XmlSec::NO_ERROR != XmlSecSingleton::Instance().validatePartialHash(&context)) {
373 LogWarning("Installation break - invalid package! >> validatePartialHash");
374 return SIGNATURE_INVALID;
378 outData.setReference(context.referenceSet);
380 if (!checkObjectReferences(outData)) {
381 LogWarning("Failed to check Object References");
382 return SIGNATURE_INVALID;
386 if (checkReferences) {
387 ReferenceValidator fileValidator(widgetContentPath);
388 if (ReferenceValidator::NO_ERROR != fileValidator.checkReferences(outData)) {
389 LogWarning("Invalid package - file references broken");
390 return SIGNATURE_INVALID;
393 } catch (const CertificateCollection::Exception::Base &e) {
394 LogError("CertificateCollection exception : " << e.DumpToString());
395 return SIGNATURE_INVALID;
396 } catch (const XmlSec::Exception::Base &e) {
397 LogError("XmlSec exception : " << e.DumpToString());
398 return SIGNATURE_INVALID;
400 LogError("Unknown exception in SignatureValidator::checkList");
401 return SIGNATURE_INVALID;
404 return disregard ? SIGNATURE_DISREGARD : SIGNATURE_VERIFIED;
408 } // namespace ValidationCore