2 * Copyright (c) 2015 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.
18 * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
19 * @author Jacek Migacz (j.migacz@samsung.com)
21 * @brief This is part of C-api proposition for cert-svc.
24 #include <sys/types.h>
37 #include <openssl/pem.h>
38 #include <openssl/ssl.h>
39 #include <openssl/x509v3.h>
40 #include <openssl/pkcs12.h>
41 #include <openssl/err.h>
42 #include <openssl/sha.h>
43 #include <openssl/evp.h>
44 #include <openssl/bio.h>
46 #include <dpl/foreach.h>
47 #include <dpl/log/log.h>
49 #include <cert-svc/cinstance.h>
50 #include <cert-svc/ccert.h>
51 #include <cert-svc/cpkcs12.h>
52 #include <cert-svc/cprimitives.h>
54 #include <vcore/Base64.h>
55 #include <vcore/Certificate.h>
56 #include <vcore/CertificateCollection.h>
57 #include <vcore/pkcs12.h>
59 #include <libxml/parser.h>
60 #include <libxml/tree.h>
62 #define START_CERT "-----BEGIN CERTIFICATE-----"
63 #define END_CERT "-----END CERTIFICATE-----"
64 #define START_TRUSTED "-----BEGIN TRUSTED CERTIFICATE-----"
65 #define END_TRUSTED "-----END TRUSTED CERTIFICATE-----"
67 using namespace ValidationCore;
71 typedef std::unique_ptr<CERT_CONTEXT, std::function<int(CERT_CONTEXT*)> > ScopedCertCtx;
73 class CertSvcInstanceImpl {
76 : m_certificateCounter(0)
78 , m_stringListCounter(0)
81 ~CertSvcInstanceImpl(){
82 FOREACH(it, m_allocatedStringSet) {
88 m_certificateCounter = 0;
89 m_certificateMap.clear();
92 m_stringListCounter = 0;
93 m_stringListMap.clear();
95 FOREACH(it, m_allocatedStringSet) {
99 m_allocatedStringSet.clear();
102 inline size_t addCert(const CertificatePtr &cert) {
103 m_certificateMap[m_certificateCounter] = cert;
104 return m_certificateCounter++;
107 inline void removeCert(const CertSvcCertificate &cert) {
108 auto iter = m_certificateMap.find(cert.privateHandler);
109 if (iter != m_certificateMap.end()) {
110 m_certificateMap.erase(iter);
114 inline int getCertFromList(
115 const CertSvcCertificateList &handler,
117 CertSvcCertificate *certificate)
119 auto iter = m_idListMap.find(handler.privateHandler);
120 if (iter == m_idListMap.end()) {
121 return CERTSVC_WRONG_ARGUMENT;
123 if (position >= iter->second.size()) {
124 return CERTSVC_WRONG_ARGUMENT;
126 certificate->privateInstance = handler.privateInstance;
127 certificate->privateHandler = (iter->second)[position];
128 return CERTSVC_SUCCESS;
131 inline int getCertListLen(const CertSvcCertificateList &handler, size_t *len) {
132 auto iter = m_idListMap.find(handler.privateHandler);
133 if (iter == m_idListMap.end() || !len) {
134 return CERTSVC_WRONG_ARGUMENT;
136 *len = (iter->second).size();
137 return CERTSVC_SUCCESS;
140 inline void removeCertList(const CertSvcCertificateList &handler) {
141 auto iter = m_idListMap.find(handler.privateHandler);
142 if (iter != m_idListMap.end())
143 m_idListMap.erase(iter);
146 inline int isSignedBy(const CertSvcCertificate &child,
147 const CertSvcCertificate &parent,
150 auto citer = m_certificateMap.find(child.privateHandler);
151 if (citer == m_certificateMap.end()) {
152 return CERTSVC_WRONG_ARGUMENT;
154 auto piter = m_certificateMap.find(parent.privateHandler);
155 if (piter == m_certificateMap.end()) {
156 return CERTSVC_WRONG_ARGUMENT;
159 if (citer->second->isSignedBy(piter->second)) {
160 *status = CERTSVC_TRUE;
162 *status = CERTSVC_FALSE;
164 return CERTSVC_SUCCESS;
167 inline int getField(const CertSvcCertificate &cert,
168 CertSvcCertificateField field,
169 CertSvcString *buffer)
171 auto iter = m_certificateMap.find(cert.privateHandler);
172 if (iter == m_certificateMap.end()) {
173 return CERTSVC_WRONG_ARGUMENT;
176 auto certPtr = iter->second;
179 case CERTSVC_SUBJECT:
180 result = certPtr->getOneLine();
183 result = certPtr->getOneLine(Certificate::FIELD_ISSUER);
185 case CERTSVC_SUBJECT_COMMON_NAME:
186 result = certPtr->getCommonName();
188 case CERTSVC_SUBJECT_COUNTRY_NAME:
189 result = certPtr->getCountryName();
191 case CERTSVC_SUBJECT_STATE_NAME:
192 result = certPtr->getStateOrProvinceName();
194 case CERTSVC_SUBJECT_ORGANIZATION_NAME:
195 result = certPtr->getOrganizationName();
197 case CERTSVC_SUBJECT_ORGANIZATION_UNIT_NAME:
198 result = certPtr->getOrganizationalUnitName();
200 case CERTSVC_SUBJECT_EMAIL_ADDRESS:
201 result = certPtr->getEmailAddres();
203 case CERTSVC_ISSUER_COMMON_NAME:
204 result = certPtr->getCommonName(Certificate::FIELD_ISSUER);
206 case CERTSVC_ISSUER_STATE_NAME:
207 result = certPtr->getStateOrProvinceName(Certificate::FIELD_ISSUER);
209 case CERTSVC_ISSUER_ORGANIZATION_NAME:
210 result = certPtr->getOrganizationName(Certificate::FIELD_ISSUER);
212 case CERTSVC_ISSUER_ORGANIZATION_UNIT_NAME:
213 result = certPtr->getOrganizationalUnitName(Certificate::FIELD_ISSUER);
215 case CERTSVC_VERSION:
217 std::stringstream stream;
218 stream << (certPtr->getVersion()+1);
219 result = stream.str();
222 case CERTSVC_SERIAL_NUMBER:
223 result = certPtr->getSerialNumberString();
225 case CERTSVC_KEY_USAGE:
226 result = certPtr->getKeyUsageString();
229 result = certPtr->getPublicKeyString();
231 case CERTSVC_SIGNATURE_ALGORITHM:
232 result = certPtr->getSignatureAlgorithmString();
238 if (result.empty()) {
239 buffer->privateHandler = NULL;
240 buffer->privateLength = 0;
241 buffer->privateInstance = cert.privateInstance;
242 return CERTSVC_SUCCESS;
245 char *cstring = new char[result.size() + 1];
246 if (cstring == NULL) {
247 buffer->privateHandler = NULL;
248 buffer->privateLength = 0;
249 buffer->privateInstance = cert.privateInstance;
250 return CERTSVC_BAD_ALLOC;
253 strncpy(cstring, result.c_str(), result.size() + 1);
255 buffer->privateHandler = cstring;
256 buffer->privateLength = result.size();
257 buffer->privateInstance = cert.privateInstance;
259 m_allocatedStringSet.insert(cstring);
261 return CERTSVC_SUCCESS;
264 inline int getNotAfter(const CertSvcCertificate &cert,
267 auto iter = m_certificateMap.find(cert.privateHandler);
268 if (iter == m_certificateMap.end()) {
269 return CERTSVC_WRONG_ARGUMENT;
271 *time = iter->second->getNotAfter();
272 return CERTSVC_SUCCESS;
275 inline int getNotBefore(const CertSvcCertificate &cert,
278 auto iter = m_certificateMap.find(cert.privateHandler);
279 if (iter == m_certificateMap.end()) {
280 return CERTSVC_WRONG_ARGUMENT;
282 *time = iter->second->getNotBefore();
283 return CERTSVC_SUCCESS;
286 inline int isRootCA(const CertSvcCertificate &cert, int *status){
287 auto iter = m_certificateMap.find(cert.privateHandler);
288 if (iter == m_certificateMap.end()) {
289 return CERTSVC_WRONG_ARGUMENT;
291 if (iter->second->isRootCert()) {
292 *status = CERTSVC_TRUE;
294 *status = CERTSVC_FALSE;
296 return CERTSVC_SUCCESS;
299 inline int getStringFromList(
300 const CertSvcStringList &handler,
302 CertSvcString *buffer)
304 buffer->privateHandler = NULL;
305 buffer->privateLength = 0;
307 auto iter = m_stringListMap.find(handler.privateHandler);
308 if (iter == m_stringListMap.end()) {
309 return CERTSVC_WRONG_ARGUMENT;
311 if (position >= iter->second.size()) {
312 return CERTSVC_WRONG_ARGUMENT;
314 const std::string &data = iter->second.at(position);
315 size_t size = data.size();
316 char *cstring = new char[size + 1];
321 strncpy(cstring, data.c_str(), size + 1);
323 buffer->privateHandler = cstring;
324 buffer->privateLength = size;
325 buffer->privateInstance = handler.privateInstance;
327 m_allocatedStringSet.insert(cstring);
329 return CERTSVC_SUCCESS;
332 inline int getStringListLen(
333 const CertSvcStringList &handler,
336 auto iter = m_stringListMap.find(handler.privateHandler);
337 if (iter == m_stringListMap.end()) {
338 return CERTSVC_WRONG_ARGUMENT;
340 *size = iter->second.size();
341 return CERTSVC_SUCCESS;
344 inline void removeStringList(const CertSvcStringList &handler)
346 m_stringListMap.erase(m_stringListMap.find(handler.privateHandler));
349 inline void removeString(const CertSvcString &handler)
351 auto iter = m_allocatedStringSet.find(handler.privateHandler);
352 if (iter != m_allocatedStringSet.end()) {
354 m_allocatedStringSet.erase(iter);
358 inline int certificateSearch(
359 CertSvcInstance instance,
360 CertSvcCertificateField field,
362 CertSvcCertificateList *handler)
364 search_field fieldId = SEARCH_FIELD_END;
367 case CERTSVC_SUBJECT:
368 fieldId = SUBJECT_STR;
371 fieldId = ISSUER_STR;
373 case CERTSVC_SUBJECT_COMMON_NAME:
374 fieldId = SUBJECT_COMMONNAME;
377 LogError("Not implemented!");
378 return CERTSVC_WRONG_ARGUMENT;
381 ScopedCertCtx ctx(cert_svc_cert_context_init(),
382 cert_svc_cert_context_final);
384 if (ctx.get() == NULL) {
385 LogWarning("Error in cert_svc_cert_context_init.");
389 LogDebug("Match string : " << value);
390 int result = cert_svc_search_certificate(ctx.get(), fieldId, const_cast<char*>(value));
391 LogDebug("Search finished!");
393 if (CERT_SVC_ERR_NO_ERROR != result) {
394 LogWarning("Error during certificate search");
399 size_t listId = m_idListCounter++;
400 std::vector<size_t> &list = m_idListMap[listId];
401 handler->privateHandler = listId;
402 handler->privateInstance = instance;
404 cert_svc_filename_list *fileList = ctx.get()->fileNames;
406 ScopedCertCtx ctx2(cert_svc_cert_context_init(),
407 cert_svc_cert_context_final);
408 if (ctx2.get() == NULL) {
409 LogWarning("Error in cert_svc_cert_context_init.");
413 // TODO add read_certifcate_from_file function to Certificate.h
414 if (CERT_SVC_ERR_NO_ERROR !=
415 cert_svc_load_file_to_context(ctx2.get(), fileList->filename))
417 LogWarning("Error in cert_svc_load_file_to_context");
421 list.push_back(addCert(CertificatePtr(new Certificate(*(ctx2.get()->certBuf)))));
423 fileList = fileList->next;
425 return CERTSVC_SUCCESS;
428 inline int sortCollection(CertSvcCertificate *certificate_array, size_t size) {
430 return CERTSVC_WRONG_ARGUMENT;
433 for (size_t i = 1; i < size; ++i) {
434 if (certificate_array[i - 1].privateInstance.privatePtr
435 != certificate_array[i].privateInstance.privatePtr)
437 return CERTSVC_WRONG_ARGUMENT;
441 CertificateList certList;
442 std::map<Certificate*, size_t> translator;
444 for (size_t i = 0; i < size; ++i) {
445 size_t pos = certificate_array[i].privateHandler;
446 auto cert = m_certificateMap.find(pos);
447 if (cert == m_certificateMap.end()) {
448 return CERTSVC_WRONG_ARGUMENT;
450 translator[cert->second.get()] = pos;
451 certList.push_back(cert->second);
454 CertificateCollection collection;
455 collection.load(certList);
457 if (!collection.sort()) {
461 auto chain = collection.getChain();
464 for (const auto &cert : collection.getChain())
465 certificate_array[i++].privateHandler = translator[cert.get()];
467 return CERTSVC_SUCCESS;
470 inline int getX509Copy(const CertSvcCertificate &certificate, X509** cert)
472 auto it = m_certificateMap.find(certificate.privateHandler);
473 if (it == m_certificateMap.end()) {
474 return CERTSVC_WRONG_ARGUMENT;
476 *cert = X509_dup(it->second->getX509());
477 return CERTSVC_SUCCESS;
480 inline int saveToFile(const CertSvcCertificate &certificate,
481 const char *location)
483 auto it = m_certificateMap.find(certificate.privateHandler);
484 if (it == m_certificateMap.end()) {
485 return CERTSVC_WRONG_ARGUMENT;
487 FILE *out = fopen(location, "w");
491 if (0 == i2d_X509_fp(out, it->second->getX509())) {
496 return CERTSVC_SUCCESS;
500 CertSvcCertificate certificate,
501 CertSvcString &message,
502 CertSvcString &signature,
503 const char *algorithm,
506 int result = CERTSVC_FAIL;
509 return CERTSVC_WRONG_ARGUMENT;
512 auto it = m_certificateMap.find(certificate.privateHandler);
513 if (it == m_certificateMap.end()) {
514 return CERTSVC_WRONG_ARGUMENT;
517 OpenSSL_add_all_digests();
520 EVP_MD_CTX* mdctx = NULL;
521 const EVP_MD * md = NULL;
522 X509 *cert = it->second->getX509();
523 EVP_PKEY *pkey = NULL;
529 pkey = X509_get_pubkey(cert);
535 if (algorithm == NULL) {
536 md = EVP_get_digestbyobj(cert->cert_info->signature->algorithm);
538 md = EVP_get_digestbyname(algorithm);
542 result = CERTSVC_INVALID_ALGORITHM;
546 mdctx = EVP_MD_CTX_create();
552 if (EVP_VerifyInit_ex(mdctx, md, NULL) != 1) {
556 if (EVP_VerifyUpdate(mdctx, message.privateHandler, message.privateLength) != 1) {
560 temp = EVP_VerifyFinal(mdctx,
561 reinterpret_cast<unsigned char*>(signature.privateHandler),
562 signature.privateLength,
566 *status = CERTSVC_INVALID_SIGNATURE;
567 result = CERTSVC_SUCCESS;
568 } else if (temp == 1) {
569 *status = CERTSVC_SUCCESS;
570 result = CERTSVC_SUCCESS;
575 EVP_MD_CTX_destroy(mdctx);
581 inline int base64Encode(
582 const CertSvcString &message,
583 CertSvcString *base64)
586 return CERTSVC_WRONG_ARGUMENT;
588 std::string info(message.privateHandler, message.privateLength);
594 char *ptr = new char[info.size()+1];
596 return CERTSVC_BAD_ALLOC;
598 memcpy(ptr, info.c_str(), info.size()+1);
599 m_allocatedStringSet.insert(ptr);
600 base64->privateHandler = ptr;
601 base64->privateLength = info.size();
602 base64->privateInstance = message.privateInstance;
603 return CERTSVC_SUCCESS;
607 const CertSvcString &base64,
608 CertSvcString *message)
611 return CERTSVC_WRONG_ARGUMENT;
613 std::string info(base64.privateHandler, base64.privateLength);
617 if (!base.finalize()) {
621 char *ptr = new char[info.size()+1];
623 return CERTSVC_BAD_ALLOC;
625 memcpy(ptr, info.c_str(), info.size()+1);
626 m_allocatedStringSet.insert(ptr);
627 message->privateHandler = ptr;
628 message->privateLength = info.size();
629 message->privateInstance = base64.privateInstance;
630 return CERTSVC_SUCCESS;
633 inline int stringNew(
634 CertSvcInstance &instance,
637 CertSvcString *output)
640 return CERTSVC_WRONG_ARGUMENT;
643 size_t allocSize = size;
645 if (allocSize == 0 || str[allocSize - 1] != 0)
648 char *ptr = new char[allocSize];
650 return CERTSVC_BAD_ALLOC;
652 memcpy(ptr, str, size);
653 ptr[allocSize - 1] = 0;
655 output->privateHandler = ptr;
656 output->privateLength = size;
657 output->privateInstance = instance;
659 m_allocatedStringSet.insert(ptr);
661 return CERTSVC_SUCCESS;
664 inline int certificateVerify(
665 CertSvcCertificate certificate,
666 const CertSvcCertificate *trusted,
668 const CertSvcCertificate *untrusted,
669 size_t untrustedSize,
673 if (!trusted || !status) {
674 return CERTSVC_WRONG_ARGUMENT;
676 auto iter = m_certificateMap.find(certificate.privateHandler);
677 if (iter == m_certificateMap.end()) {
678 return CERTSVC_WRONG_ARGUMENT;
681 X509 *cert = iter->second->getX509();
682 X509_STORE *store = X509_STORE_new();
683 STACK_OF(X509) *ustore = sk_X509_new_null();
685 for (size_t i = 0; i < trustedSize; ++i) {
686 auto iter = m_certificateMap.find(trusted[i].privateHandler);
687 if (iter == m_certificateMap.end()) {
688 X509_STORE_free(store);
689 sk_X509_free(ustore);
690 return CERTSVC_WRONG_ARGUMENT;
693 X509_STORE_add_cert(store, iter->second->getX509());
696 for (size_t i = 0; i < untrustedSize; ++i) {
697 auto iter = m_certificateMap.find(untrusted[i].privateHandler);
698 if (iter == m_certificateMap.end()) {
699 X509_STORE_free(store);
700 sk_X509_free(ustore);
701 return CERTSVC_WRONG_ARGUMENT;
704 if (sk_X509_push(ustore, iter->second->getX509()) == 0)
708 X509_STORE_CTX context;
709 X509_STORE_CTX_init(&context, store, cert, ustore);
710 int result = X509_verify_cert(&context);
712 if (result == 1 && checkCaFlag) { // check strictly
713 STACK_OF(X509) *resultChain = X509_STORE_CTX_get1_chain(&context);
714 X509* tmpCert = NULL;
716 while ((tmpCert = sk_X509_pop(resultChain))) {
717 caFlagValidity = X509_check_ca(tmpCert);
718 if (caFlagValidity != 1 && (tmpCert = sk_X509_pop(resultChain)) != NULL) {
719 // the last one is not a CA.
726 X509_STORE_CTX_cleanup(&context);
727 X509_STORE_free(store);
728 sk_X509_free(ustore);
731 *status = CERTSVC_SUCCESS;
733 *status = CERTSVC_FAIL;
735 return CERTSVC_SUCCESS;
738 int getVisibility(CertSvcCertificate certificate, CertSvcVisibility *visibility)
740 int ret = CERTSVC_FAIL;
741 //xmlChar *xmlPathCertificateSet = (xmlChar*) "CertificateSet"; /*unused variable*/
742 //xmlChar *xmlPathCertificateDomain = (xmlChar*) "CertificateDomain";// name=\"tizen-platform\""; /*unused variable*/
743 xmlChar *xmlPathDomainPlatform = (xmlChar*) "tizen-platform";
744 xmlChar *xmlPathDomainPublic = (xmlChar*) "tizen-public";
745 xmlChar *xmlPathDomainPartner = (xmlChar*) "tizen-partner";
746 xmlChar *xmlPathDomainDeveloper = (xmlChar*) "tizen-developer";
747 //xmlChar *xmlPathFingerPrintSHA1 = (xmlChar*) "FingerprintSHA1"; /*unused variable*/
749 auto iter = m_certificateMap.find(certificate.privateHandler);
750 if (iter == m_certificateMap.end()) {
753 CertificatePtr certPtr = iter->second;
755 std::string fingerprint = Certificate::FingerprintToColonHex(certPtr->getFingerprint(Certificate::FINGERPRINT_SHA1));
758 xmlDocPtr doc = xmlParseFile(FINGERPRINT_LIST_PATH);
759 if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL))
761 LogError("Failed to prase fingerprint_list.xml");
762 return CERTSVC_IO_ERROR;
765 xmlNodePtr curPtr = xmlFirstElementChild(xmlDocGetRootElement(doc));
768 LogError("Can not find root");
769 ret = CERTSVC_IO_ERROR;
773 while(curPtr != NULL)
775 xmlAttr* attr = curPtr->properties;
776 if(!attr->children || !attr->children->content)
778 LogError("Failed to get fingerprints from list");
783 xmlChar* strLevel = attr->children->content;
784 xmlNodePtr FpPtr = xmlFirstElementChild(curPtr);
787 LogError("Could not find fingerprint");
792 LogDebug("Retrieve level : " << strLevel);
795 xmlChar *content = xmlNodeGetContent(FpPtr);
796 if(xmlStrcmp(content, (xmlChar*)fingerprint.c_str()) == 0)
798 LogDebug("fingerprint : " << content << " are " << strLevel);
799 if(!xmlStrcmp(strLevel, xmlPathDomainPlatform))
801 *visibility = CERTSVC_VISIBILITY_PLATFORM;
802 ret = CERTSVC_SUCCESS;
805 else if(!xmlStrcmp(strLevel, xmlPathDomainPublic))
807 *visibility = CERTSVC_VISIBILITY_PUBLIC;
808 ret = CERTSVC_SUCCESS;
811 else if(!xmlStrcmp(strLevel, xmlPathDomainPartner))
813 *visibility = CERTSVC_VISIBILITY_PARTNER;
814 ret = CERTSVC_SUCCESS;
817 else if(!xmlStrcmp(strLevel, xmlPathDomainDeveloper))
819 *visibility = CERTSVC_VISIBILITY_DEVELOPER;
820 ret = CERTSVC_SUCCESS;
824 FpPtr = xmlNextElementSibling(FpPtr);
826 curPtr = xmlNextElementSibling(curPtr);
835 inline int pkcsNameIsUniqueInStore(
836 CertStoreType storeType,
837 CertSvcString pfxIdString,
840 return c_certsvc_pkcs12_alias_exists_in_store(storeType, pfxIdString.privateHandler, is_unique);
843 inline int getCertDetailFromStore(CertStoreType storeType,
848 return c_certsvc_pkcs12_get_certificate_buffer_from_store(storeType, gname.privateHandler, certBuffer, certSize);
851 inline int pkcsDeleteCertFromStore(
852 CertStoreType storeType,
856 return c_certsvc_pkcs12_delete_certificate_from_store(storeType, gname.privateHandler);
859 inline int pkcsHasPassword(
860 CertSvcString filepath,
863 return c_certsvc_pkcs12_has_password(filepath.privateHandler, has_password);
866 inline int pkcsImportToStore(
867 CertStoreType storeType,
870 CertSvcString pfxIdString)
872 return c_certsvc_pkcs12_import_from_file_to_store(storeType, path.privateHandler, pass.privateHandler, pfxIdString.privateHandler);
875 inline int pkcsGetAliasNameForCertInStore(CertStoreType storeType,
879 return c_certsvc_pkcs12_get_certificate_alias_from_store(storeType, gname.privateHandler, alias);
882 inline int pkcsSetCertStatusToStore(CertStoreType storeType,
887 return c_certsvc_pkcs12_set_certificate_status_to_store(storeType, is_root_app, gname.privateHandler, status);
890 inline int pkcsGetCertStatusFromStore(
891 CertStoreType storeType,
895 return c_certsvc_pkcs12_get_certificate_status_from_store(storeType, gname.privateHandler, status);
898 inline int getCertFromStore(CertSvcInstance instance,
899 CertStoreType storeType,
901 CertSvcCertificate *certificate)
903 return certsvc_get_certificate(instance, storeType, gname, certificate);
906 inline int freePkcsIdListFromStore(
907 CertSvcStoreCertList** certList)
909 return c_certsvc_pkcs12_free_aliases_loaded_from_store(certList);
912 inline int getPkcsIdListFromStore(
913 CertStoreType storeType,
915 CertSvcStoreCertList** certList,
918 return c_certsvc_pkcs12_get_certificate_list_from_store(storeType, is_root_app, certList, length);
921 inline int getPkcsIdEndUserListFromStore(
922 CertStoreType storeType,
923 CertSvcStoreCertList** certList,
926 return c_certsvc_pkcs12_get_end_user_certificate_list_from_store(storeType, certList, length);
929 inline int getPkcsIdRootListFromStore(
930 CertStoreType storeType,
931 CertSvcStoreCertList** certList,
934 return c_certsvc_pkcs12_get_root_certificate_list_from_store(storeType, certList, length);
937 inline int getPkcsPrivateKeyFromStore(
938 CertStoreType storeType,
943 return c_certsvc_pkcs12_private_key_load_from_store(storeType, gname.privateHandler, certBuffer, certSize);
946 inline int getPkcsCertificateListFromStore(
947 CertSvcInstance &instance,
948 CertStoreType storeType,
949 CertSvcString &pfxIdString,
950 CertSvcCertificateList *handler)
954 int result = c_certsvc_pkcs12_load_certificates_from_store(storeType, pfxIdString.privateHandler, &certs, &ncerts);
955 if (result != CERTSVC_SUCCESS) {
956 LogError("Unable to load certificates from store.");
960 std::vector<CertificatePtr> certPtrVector;
962 for (size_t i = 0; i < ncerts; i++) {
963 Alias.privateHandler = certs[i];
964 Alias.privateLength = strlen(certs[i]);
965 char *certBuffer = NULL;
966 size_t certLength = 0;
967 result = certsvc_pkcs12_get_certificate_info_from_store(instance, storeType, Alias, &certBuffer, &certLength);
968 if (result != CERTSVC_SUCCESS || !certBuffer) {
969 LogError("Failed to get certificate buffer.");
973 const char *header = strstr(certBuffer, START_CERT);
974 const char *headEnd = START_CERT;
975 const char *trailer = NULL;
976 const char *tailEnd = NULL;
978 // START_CERT not found. let's find START_TRUSTED.
979 header = strstr(certBuffer, START_TRUSTED);
980 headEnd = START_TRUSTED;
984 // START_something found. let's find END_CERT first.
985 trailer = strstr(header, END_CERT);
990 // END_CERT not found. let's find END_TRUSTED.
991 trailer = strstr(header, END_TRUSTED);
992 tailEnd = END_TRUSTED;
996 LogError("Failed the get the certificate.");
1000 size_t length = ((1 + strlen(header)) - (strlen(headEnd) + strlen(tailEnd) + 1));
1001 std::string tmpBuffer(certBuffer);
1002 tmpBuffer = tmpBuffer.substr(strlen(headEnd), length);
1003 std::string binary(tmpBuffer.c_str(), length);
1004 certPtrVector.push_back(CertificatePtr(new Certificate(binary, Certificate::FORM_BASE64)));
1009 c_certsvc_pkcs12_free_certificates(certs);
1011 std::vector<size_t> listId;
1012 for (const auto &cert : certPtrVector)
1013 listId.push_back(addCert(cert));
1015 size_t position = m_idListCounter++;
1016 m_idListMap[position] = listId;
1018 handler->privateInstance = instance;
1019 handler->privateHandler = position;
1024 inline bool checkValidStoreType(CertStoreType storeType)
1026 if (storeType >= VPN_STORE && storeType <= ALL_STORE)
1033 size_t m_certificateCounter;
1034 std::map<size_t, CertificatePtr> m_certificateMap;
1036 size_t m_idListCounter;
1037 std::map<size_t, std::vector<size_t> > m_idListMap;
1039 size_t m_stringListCounter;
1040 std::map<size_t, std::vector<std::string> > m_stringListMap;
1042 std::set<char *> m_allocatedStringSet;
1045 inline CertSvcInstanceImpl *impl(CertSvcInstance instance) {
1046 return static_cast<CertSvcInstanceImpl*>(instance.privatePtr);
1049 } // namespace anonymous
1051 int certsvc_instance_new(CertSvcInstance *instance) {
1052 static int init = 1;
1054 SSL_library_init(); // required by message verification
1055 OpenSSL_add_all_digests();
1059 instance->privatePtr =
1060 reinterpret_cast<void*>(new CertSvcInstanceImpl);
1061 if (instance->privatePtr)
1062 return CERTSVC_SUCCESS;
1063 } catch (std::bad_alloc &) {
1064 return CERTSVC_BAD_ALLOC;
1066 return CERTSVC_FAIL;
1069 void certsvc_instance_reset(CertSvcInstance instance) {
1070 impl(instance)->reset();
1073 void certsvc_instance_free(CertSvcInstance instance) {
1074 delete impl(instance);
1077 int certsvc_certificate_new_from_file(
1078 CertSvcInstance instance,
1079 const char *location,
1080 CertSvcCertificate *certificate)
1083 ScopedCertCtx context(cert_svc_cert_context_init(),
1084 cert_svc_cert_context_final);
1086 int result = cert_svc_load_file_to_context(context.get(), location);
1089 case CERT_SVC_ERR_INVALID_PARAMETER: return CERTSVC_WRONG_ARGUMENT;
1090 case CERT_SVC_ERR_INVALID_OPERATION: return CERTSVC_FAIL;
1091 case CERT_SVC_ERR_MEMORY_ALLOCATION: return CERTSVC_BAD_ALLOC;
1095 CertificatePtr cert(new Certificate(*(context->certBuf)));
1097 certificate->privateInstance = instance;
1098 certificate->privateHandler = impl(instance)->addCert(cert);
1100 return CERTSVC_SUCCESS;
1101 // TODO support for std exceptions
1102 } catch (std::bad_alloc &) {
1103 return CERTSVC_BAD_ALLOC;
1105 return CERTSVC_FAIL;
1108 int certsvc_certificate_new_from_memory(
1109 CertSvcInstance instance,
1110 const unsigned char *memory,
1112 CertSvcCertificateForm form,
1113 CertSvcCertificate *certificate)
1116 Certificate::FormType formType;
1117 std::string binary((char*)memory, len);
1119 if (CERTSVC_FORM_DER == form) {
1120 formType = Certificate::FORM_DER;
1122 formType = Certificate::FORM_BASE64;
1125 CertificatePtr cert(new Certificate(binary, formType));
1127 certificate->privateInstance = instance;
1128 certificate->privateHandler = impl(instance)->addCert(cert);
1129 return CERTSVC_SUCCESS;
1130 } catch (std::bad_alloc &) {
1131 return CERTSVC_BAD_ALLOC;
1133 return CERTSVC_FAIL;
1136 void certsvc_certificate_free(CertSvcCertificate certificate)
1138 if (certificate.privateHandler != 0)
1139 impl(certificate.privateInstance)->removeCert(certificate);
1142 int certsvc_certificate_save_file(
1143 CertSvcCertificate certificate,
1144 const char *location)
1146 return impl(certificate.privateInstance)->saveToFile(certificate, location);
1149 int certsvc_certificate_search(
1150 CertSvcInstance instance,
1151 CertSvcCertificateField field,
1153 CertSvcCertificateList *handler)
1156 return impl(instance)->certificateSearch(instance, field, value, handler);
1157 } catch (std::bad_alloc &) {
1158 return CERTSVC_BAD_ALLOC;
1160 return CERTSVC_FAIL;
1163 int certsvc_certificate_list_get_one(
1164 CertSvcCertificateList handler,
1166 CertSvcCertificate *certificate)
1168 return impl(handler.privateInstance)->
1169 getCertFromList(handler, position, certificate);
1172 int certsvc_certificate_list_get_length(
1173 CertSvcCertificateList handler,
1176 return impl(handler.privateInstance)->getCertListLen(handler, size);
1179 void certsvc_certificate_list_free(CertSvcCertificateList handler)
1181 impl(handler.privateInstance)->removeCertList(handler);
1184 int certsvc_certificate_is_signed_by(
1185 CertSvcCertificate child,
1186 CertSvcCertificate parent,
1189 if (child.privateInstance.privatePtr == parent.privateInstance.privatePtr) {
1190 return impl(child.privateInstance)->isSignedBy(child, parent, status);
1192 return CERTSVC_WRONG_ARGUMENT;
1195 int certsvc_certificate_get_string_field(
1196 CertSvcCertificate certificate,
1197 CertSvcCertificateField field,
1198 CertSvcString *buffer)
1201 return impl(certificate.privateInstance)->getField(certificate, field, buffer);
1202 } catch (std::bad_alloc &) {
1203 return CERTSVC_BAD_ALLOC;
1205 return CERTSVC_FAIL;
1208 int certsvc_certificate_get_not_after(
1209 CertSvcCertificate certificate,
1213 return impl(certificate.privateInstance)->getNotAfter(certificate, result);
1215 return CERTSVC_FAIL;
1218 int certsvc_certificate_get_not_before(
1219 CertSvcCertificate certificate,
1223 return impl(certificate.privateInstance)->getNotBefore(certificate, result);
1225 return CERTSVC_FAIL;
1228 int certsvc_certificate_is_root_ca(CertSvcCertificate certificate, int *status)
1230 return impl(certificate.privateInstance)->isRootCA(certificate, status);
1233 int certsvc_string_list_get_one(
1234 CertSvcStringList handler,
1236 CertSvcString *buffer)
1239 return impl(handler.privateInstance)->getStringFromList(handler, position, buffer);
1240 } catch (std::bad_alloc &) {
1241 return CERTSVC_BAD_ALLOC;
1243 return CERTSVC_FAIL;
1246 int certsvc_string_list_get_length(
1247 CertSvcStringList handler,
1250 return impl(handler.privateInstance)->getStringListLen(handler, size);
1253 void certsvc_string_list_free(CertSvcStringList handler)
1255 if (handler.privateHandler != 0)
1257 impl(handler.privateInstance)->removeStringList(handler);
1258 handler.privateHandler = 0;
1262 void certsvc_string_free(CertSvcString string)
1264 if (string.privateHandler)
1265 impl(string.privateInstance)->removeString(string);
1268 void certsvc_string_to_cstring(
1269 CertSvcString string,
1270 const char **buffer,
1274 *buffer = string.privateHandler;
1277 *len = string.privateLength;
1281 int certsvc_certificate_chain_sort(
1282 CertSvcCertificate *certificate_array,
1286 if (!certificate_array) {
1287 return CERTSVC_WRONG_ARGUMENT;
1289 return impl(certificate_array[0].privateInstance)->
1290 sortCollection(certificate_array, size);
1291 } catch (std::bad_alloc &) {
1292 return CERTSVC_BAD_ALLOC;
1294 return CERTSVC_FAIL;
1297 int certsvc_certificate_dup_x509(CertSvcCertificate certificate, X509 **cert)
1300 return impl(certificate.privateInstance)->getX509Copy(certificate, cert);
1302 return CERTSVC_FAIL;
1305 void certsvc_certificate_free_x509(X509 *x509)
1311 void certsvc_pkcs12_free_evp_pkey(EVP_PKEY* pkey)
1313 EVP_PKEY_free(pkey);
1316 int certsvc_message_verify(
1317 CertSvcCertificate certificate,
1318 CertSvcString message,
1319 CertSvcString signature,
1320 const char *algorithm,
1324 return impl(certificate.privateInstance)->verify(
1331 return CERTSVC_FAIL;
1334 int certsvc_base64_encode(CertSvcString message, CertSvcString *base64)
1337 return impl(message.privateInstance)->base64Encode(message, base64);
1339 return CERTSVC_FAIL;
1342 int certsvc_base64_decode(CertSvcString base64, CertSvcString *message)
1345 return impl(base64.privateInstance)->base64Decode(base64, message);
1347 return CERTSVC_FAIL;
1350 int certsvc_string_new(
1351 CertSvcInstance instance,
1354 CertSvcString *output)
1357 return impl(instance)->stringNew(instance, url, size, output);
1359 return CERTSVC_FAIL;
1362 int certsvc_string_not_managed(
1363 CertSvcInstance instance,
1366 CertSvcString *output)
1369 return CERTSVC_WRONG_ARGUMENT;
1371 output->privateHandler = const_cast<char*>(url);
1372 output->privateLength = size;
1373 output->privateInstance = instance;
1374 return CERTSVC_SUCCESS;
1377 int certsvc_certificate_verify(
1378 CertSvcCertificate certificate,
1379 const CertSvcCertificate *trusted,
1381 const CertSvcCertificate *untrusted,
1382 size_t untrustedSize,
1386 return impl(certificate.privateInstance)->certificateVerify(
1395 return CERTSVC_FAIL;
1398 int certsvc_certificate_verify_with_caflag(
1399 CertSvcCertificate certificate,
1400 const CertSvcCertificate *trusted,
1402 const CertSvcCertificate *untrusted,
1403 size_t untrustedSize,
1407 return impl(certificate.privateInstance)->certificateVerify(
1416 return CERTSVC_FAIL;
1419 int certsvc_certificate_get_visibility(CertSvcCertificate certificate, CertSvcVisibility *visibility)
1422 return impl(certificate.privateInstance)->getVisibility(certificate, visibility);
1425 LogError("exception occur");
1427 return CERTSVC_FAIL;
1430 int certsvc_get_certificate(CertSvcInstance instance,
1431 CertStoreType storeType,
1433 CertSvcCertificate *certificate)
1435 int result = CERTSVC_SUCCESS;
1436 char* certBuffer = NULL;
1437 std::string fileName;
1439 FILE* fp_write = NULL;
1441 X509* x509Struct = NULL;
1444 result = c_certsvc_pkcs12_get_certificate_buffer_from_store(storeType, gname, &certBuffer, &length);
1445 if (result != CERTSVC_SUCCESS) {
1446 LogError("Failed to get certificate buffer from store.");
1450 pBio = BIO_new(BIO_s_mem());
1452 LogError("Failed to allocate memory.");
1453 result = CERTSVC_BAD_ALLOC;
1456 length = BIO_write(pBio, (const void*) certBuffer, length);
1458 LogError("Failed to load cert into bio.");
1459 result = CERTSVC_BAD_ALLOC;
1462 x509Struct = PEM_read_bio_X509(pBio, NULL, 0, NULL);
1463 if (x509Struct != NULL) {
1464 CertificatePtr cert(new Certificate(x509Struct));
1465 certificate->privateInstance = instance;
1466 certificate->privateHandler = impl(instance)->addCert(cert);
1467 if (certBuffer!=NULL) free(certBuffer);
1470 fileName.append(CERTSVC_PKCS12_STORAGE_DIR);
1471 fileName.append(gname);
1472 if (!(fp_write = fopen(fileName.c_str(), "w"))) {
1473 LogError("Failed to open the file for writing, [" << fileName << "].");
1474 result = CERTSVC_FAIL;
1478 if (fwrite(certBuffer, sizeof(char), (size_t)length, fp_write) != (size_t)length) {
1479 LogError("Fail to write certificate.");
1480 result = CERTSVC_FAIL;
1485 result = certsvc_certificate_new_from_file(instance, fileName.c_str(), certificate);
1486 if (result != CERTSVC_SUCCESS) {
1487 LogError("Failed to construct certificate from buffer.");
1490 unlink(fileName.c_str());
1492 result = CERTSVC_SUCCESS;
1493 } catch (std::bad_alloc &) {
1494 return CERTSVC_BAD_ALLOC;
1498 if (x509Struct) X509_free(x509Struct);
1499 if (pBio) BIO_free(pBio);
1503 int certsvc_pkcs12_check_alias_exists_in_store(CertSvcInstance instance,
1504 CertStoreType storeType,
1505 CertSvcString pfxIdString,
1508 if (pfxIdString.privateHandler == NULL || pfxIdString.privateLength<=0) {
1509 LogError("Invalid input parameter.");
1510 return CERTSVC_WRONG_ARGUMENT;
1514 if (!impl(instance)->checkValidStoreType(storeType)) {
1515 LogError("Invalid input parameter.");
1516 return CERTSVC_INVALID_STORE_TYPE;
1519 return impl(instance)->pkcsNameIsUniqueInStore(storeType, pfxIdString, is_unique);
1521 return CERTSVC_FAIL;
1524 int certsvc_pkcs12_free_certificate_list_loaded_from_store(CertSvcInstance instance,
1525 CertSvcStoreCertList **certList)
1527 if (certList == NULL || *certList == NULL) {
1528 LogError("Invalid input parameter.");
1529 return CERTSVC_WRONG_ARGUMENT;
1533 return impl(instance)->freePkcsIdListFromStore(certList);
1535 return CERTSVC_FAIL;
1538 int certsvc_pkcs12_get_certificate_list_from_store(CertSvcInstance instance,
1539 CertStoreType storeType,
1541 CertSvcStoreCertList **certList,
1544 if (certList == NULL || *certList != NULL) {
1545 LogError("Invalid input parameter.");
1546 return CERTSVC_WRONG_ARGUMENT;
1550 if (!impl(instance)->checkValidStoreType(storeType)) {
1551 LogError("Invalid input parameter.");
1552 return CERTSVC_INVALID_STORE_TYPE;
1555 return impl(instance)->getPkcsIdListFromStore(storeType, is_root_app, certList, length);
1558 return CERTSVC_FAIL;
1561 int certsvc_pkcs12_get_end_user_certificate_list_from_store(CertSvcInstance instance,
1562 CertStoreType storeType,
1563 CertSvcStoreCertList **certList,
1566 if (certList == NULL || *certList != NULL) {
1567 LogError("Invalid input parameter.");
1568 return CERTSVC_WRONG_ARGUMENT;
1572 if (!impl(instance)->checkValidStoreType(storeType)) {
1573 LogError("Invalid input parameter.");
1574 return CERTSVC_INVALID_STORE_TYPE;
1577 return impl(instance)->getPkcsIdEndUserListFromStore(storeType, certList, length);
1579 return CERTSVC_FAIL;
1582 int certsvc_pkcs12_get_root_certificate_list_from_store(CertSvcInstance instance,
1583 CertStoreType storeType,
1584 CertSvcStoreCertList **certList,
1587 if (certList == NULL || *certList != NULL) {
1588 LogError("Invalid input parameter.");
1589 return CERTSVC_WRONG_ARGUMENT;
1593 if (!impl(instance)->checkValidStoreType(storeType)) {
1594 LogError("Invalid input parameter.");
1595 return CERTSVC_INVALID_STORE_TYPE;
1598 return impl(instance)->getPkcsIdRootListFromStore(storeType, certList, length);
1600 return CERTSVC_FAIL;
1603 int certsvc_pkcs12_get_certificate_info_from_store(CertSvcInstance instance,
1604 CertStoreType storeType,
1605 CertSvcString gname,
1609 if (certBuffer == NULL || *certBuffer != NULL) {
1610 LogError("Invalid input parameter.");
1611 return CERTSVC_WRONG_ARGUMENT;
1615 if (!impl(instance)->checkValidStoreType(storeType)) {
1616 LogError("Invalid input parameter.");
1617 return CERTSVC_INVALID_STORE_TYPE;
1620 return impl(instance)->getCertDetailFromStore(storeType, gname, certBuffer, certSize);
1622 return CERTSVC_FAIL;
1625 int certsvc_pkcs12_delete_certificate_from_store(CertSvcInstance instance,
1626 CertStoreType storeType,
1627 CertSvcString gname)
1630 if (!impl(instance)->checkValidStoreType(storeType)) {
1631 LogError("Invalid input parameter.");
1632 return CERTSVC_INVALID_STORE_TYPE;
1634 return impl(instance)->pkcsDeleteCertFromStore(storeType, gname);
1636 return CERTSVC_FAIL;
1639 int certsvc_pkcs12_import_from_file_to_store(CertSvcInstance instance,
1640 CertStoreType storeType,
1642 CertSvcString password,
1643 CertSvcString pfxIdString)
1646 if (path.privateHandler != NULL) {
1647 if (!impl(instance)->checkValidStoreType(storeType)) {
1648 LogError("Invalid input parameter.");
1649 return CERTSVC_INVALID_STORE_TYPE;
1651 return impl(instance)->pkcsImportToStore(storeType, path, password, pfxIdString);
1654 return CERTSVC_FAIL;
1656 return CERTSVC_FAIL;
1659 int certsvc_pkcs12_get_alias_name_for_certificate_in_store(CertSvcInstance instance,
1660 CertStoreType storeType,
1661 CertSvcString gname,
1664 if (gname.privateHandler == NULL || gname.privateLength<=0) {
1665 LogError("Invalid input parameter.");
1666 return CERTSVC_WRONG_ARGUMENT;
1670 if (!impl(instance)->checkValidStoreType(storeType)) {
1671 LogError("Invalid input parameter.");
1672 return CERTSVC_INVALID_STORE_TYPE;
1674 return impl(instance)->pkcsGetAliasNameForCertInStore(storeType, gname, alias);
1676 return CERTSVC_FAIL;
1679 int certsvc_pkcs12_set_certificate_status_to_store(CertSvcInstance instance,
1680 CertStoreType storeType,
1682 CertSvcString gname,
1686 if (!impl(instance)->checkValidStoreType(storeType)) {
1687 LogError("Invalid input parameter.");
1688 return CERTSVC_INVALID_STORE_TYPE;
1690 return impl(instance)->pkcsSetCertStatusToStore(storeType, is_root_app, gname, status);
1692 return CERTSVC_FAIL;
1695 int certsvc_pkcs12_get_certificate_status_from_store(
1696 CertSvcInstance instance,
1697 CertStoreType storeType,
1698 CertSvcString gname,
1702 if (!impl(instance)->checkValidStoreType(storeType)) {
1703 LogError("Invalid input parameter.");
1704 return CERTSVC_INVALID_STORE_TYPE;
1706 return impl(instance)->pkcsGetCertStatusFromStore(storeType, gname, status);
1708 return CERTSVC_FAIL;
1711 int certsvc_pkcs12_get_certificate_from_store(CertSvcInstance instance,
1712 CertStoreType storeType,
1714 CertSvcCertificate *certificate)
1717 if (!impl(instance)->checkValidStoreType(storeType)) {
1718 LogError("Invalid input parameter.");
1719 return CERTSVC_INVALID_STORE_TYPE;
1721 return impl(instance)->getCertFromStore(instance, storeType, gname, certificate);
1723 return CERTSVC_FAIL;
1726 int certsvc_pkcs12_load_certificate_list_from_store(
1727 CertSvcInstance instance,
1728 CertStoreType storeType,
1729 CertSvcString pfxIdString,
1730 CertSvcCertificateList *certificateList)
1733 if (!impl(instance)->checkValidStoreType(storeType)) {
1734 LogError("Invalid input parameter.");
1735 return CERTSVC_INVALID_STORE_TYPE;
1737 return impl(instance)->getPkcsCertificateListFromStore(instance, storeType, pfxIdString, certificateList);
1739 return CERTSVC_FAIL;
1742 int certsvc_pkcs12_private_key_dup_from_store(
1743 CertSvcInstance instance,
1744 CertStoreType storeType,
1745 CertSvcString gname,
1750 if (!impl(instance)->checkValidStoreType(storeType)) {
1751 LogError("Invalid input parameter.");
1752 return CERTSVC_INVALID_STORE_TYPE;
1754 return impl(instance)->getPkcsPrivateKeyFromStore(storeType, gname, certBuffer, certSize);
1756 return CERTSVC_FAIL;
1759 int certsvc_pkcs12_dup_evp_pkey_from_store(
1760 CertSvcInstance instance,
1761 CertStoreType storeType,
1762 CertSvcString gname,
1765 char *buffer = NULL;
1768 int result = certsvc_pkcs12_private_key_dup_from_store(instance, storeType, gname, &buffer, &size);
1769 if (result != CERTSVC_SUCCESS) {
1770 LogError("Error in certsvc_pkcs12_private_key_dup");
1774 BIO *b = BIO_new(BIO_s_mem());
1775 if ((int)size != BIO_write(b, buffer, size)) {
1776 LogError("Error in BIO_write");
1778 certsvc_pkcs12_private_key_free(buffer);
1779 return CERTSVC_FAIL;
1782 certsvc_pkcs12_private_key_free(buffer);
1783 *pkey = PEM_read_bio_PrivateKey(b, NULL, NULL, NULL);
1786 return CERTSVC_SUCCESS;
1788 LogError("Result is null. Openssl REASON code is : " << ERR_GET_REASON(ERR_peek_last_error()));
1789 return CERTSVC_FAIL;
1792 int certsvc_pkcs12_has_password(
1793 CertSvcInstance instance,
1794 CertSvcString filepath,
1798 return impl(instance)->pkcsHasPassword(
1802 return CERTSVC_FAIL;
1805 void certsvc_pkcs12_private_key_free(char *buffer)