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.
17 * @file tpkp_gnutls.cpp
18 * @author Kyungwook Tak (k.tak@samsung.com)
20 * @brief Tizen Https Public Key Pinning implementation for gnutls.
22 #include "tpkp_gnutls.h"
29 #include <gnutls/gnutls.h>
30 #include <gnutls/abstract.h>
31 #include <gnutls/x509.h>
33 #include "tpkp_common.h"
34 #include "tpkp_client_cache.h"
38 TPKP::ClientCache g_cache;
40 inline int err_tpkp_to_gnutlse(tpkp_e err) noexcept
43 case TPKP_E_NONE: return GNUTLS_E_SUCCESS;
44 case TPKP_E_MEMORY: return GNUTLS_E_MEMORY_ERROR;
45 case TPKP_E_INVALID_URL: return GNUTLS_E_INVALID_SESSION;
46 case TPKP_E_NO_URL_DATA: return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
47 case TPKP_E_PUBKEY_MISMATCH: return GNUTLS_E_CERTIFICATE_KEY_MISMATCH;
48 case TPKP_E_INVALID_CERT:
49 case TPKP_E_INVALID_PEER_CERT_CHAIN:
50 case TPKP_E_FAILED_GET_PUBKEY_HASH: return GNUTLS_E_PK_SIG_VERIFY_FAILED;
51 case TPKP_E_CERT_VERIFICATION_FAILED: return GNUTLS_E_CERTIFICATE_ERROR;
52 case TPKP_E_STD_EXCEPTION:
54 default: return GNUTLS_E_INTERNAL_ERROR;
58 using GnutlsX509Ptr = std::unique_ptr<gnutls_x509_crt_t, void(*)(gnutls_x509_crt_t *)>;
59 inline GnutlsX509Ptr createGnutlsX509Ptr(void)
61 return GnutlsX509Ptr(new gnutls_x509_crt_t, [](gnutls_x509_crt_t *ptr) {
62 if (!!ptr) gnutls_x509_crt_deinit(*ptr);
66 TPKP::RawBuffer getPubkeyHash(gnutls_x509_crt_t cert, TPKP::HashAlgo algo)
68 std::unique_ptr<gnutls_pubkey_t, void(*)(gnutls_pubkey_t *)>
69 pubkeyPtr(new gnutls_pubkey_t, [](gnutls_pubkey_t *ptr)->void
72 gnutls_pubkey_deinit(*ptr);
75 int ret = gnutls_pubkey_init(pubkeyPtr.get());
76 TPKP_CHECK_THROW_EXCEPTION(ret == GNUTLS_E_SUCCESS,
78 "Failed to gnutls_pubkey_init. gnutls ret: " << ret);
80 ret = gnutls_pubkey_import_x509(*pubkeyPtr, cert, 0);
81 TPKP_CHECK_THROW_EXCEPTION(ret == GNUTLS_E_SUCCESS,
83 "Failed to gnutls_pubkey_import_x509. gnutls ret: " << ret);
86 ret = gnutls_pubkey_export(*pubkeyPtr, GNUTLS_X509_FMT_DER, nullptr, &len);
87 TPKP_CHECK_THROW_EXCEPTION(
88 (ret == GNUTLS_E_SHORT_MEMORY_BUFFER || ret == GNUTLS_E_SUCCESS) && len != 0,
90 "Failed to gnutls_pubkey_export for getting size. gnutls ret: "
91 << ret << " desc: " << gnutls_strerror(ret) << " size: " << len);
93 TPKP::RawBuffer derbuf(len, 0x00);
94 ret = gnutls_pubkey_export(*pubkeyPtr, GNUTLS_X509_FMT_DER, derbuf.data(), &len);
95 TPKP_CHECK_THROW_EXCEPTION(ret == GNUTLS_E_SUCCESS && len == derbuf.size(),
97 "Failed to gnutls_pubkey_export. gnutls ret: "
98 << ret << " desc: " << gnutls_strerror(ret));
100 gnutls_datum_t pubkeyder = {
102 static_cast<unsigned int>(derbuf.size())
105 auto gnutlsHashAlgo = GNUTLS_DIG_SHA1; /* default hash alog */
108 case TPKP::HashAlgo::SHA1:
109 out.resize(TPKP::typeCast(TPKP::HashSize::SHA1), 0x00);
111 gnutlsHashAlgo = GNUTLS_DIG_SHA1;
115 TPKP_CHECK_THROW_EXCEPTION(
118 "Invalid hash algo type in getPubkeyHash.");
121 ret = gnutls_fingerprint(gnutlsHashAlgo, &pubkeyder, out.data(), &len);
122 TPKP_CHECK_THROW_EXCEPTION(ret == GNUTLS_E_SUCCESS && len == out.size(),
123 TPKP_E_FAILED_GET_PUBKEY_HASH,
124 "Failed to gnutls_fingerprint. gnutls ret: "
125 << ret << " desc: " << gnutls_strerror(ret));
130 GnutlsX509Ptr d2iCert(const gnutls_datum_t *datum)
132 auto crtPtr = createGnutlsX509Ptr();
134 TPKP_CHECK_THROW_EXCEPTION(
135 gnutls_x509_crt_init(crtPtr.get()) == GNUTLS_E_SUCCESS,
136 TPKP_E_INTERNAL, "Failed to gnutls_x509_crt_init.");
137 TPKP_CHECK_THROW_EXCEPTION(
138 gnutls_x509_crt_import(*crtPtr, datum, GNUTLS_X509_FMT_DER) >= 0,
139 TPKP_E_INTERNAL, "Failed to import DER to gnutls crt");
145 * Need not to gnutls_x509_crt_deinit for returned value unless GNUTLS_TL_GET_COPY
147 * Refer API description of gnutls_certificate_get_issuer.
149 * gnutls_certificate_get_issuer will return the issuer of a given certificate.
150 * As with gnutls_x509_trust_list_get_issuer() this functions requires the
151 * GNUTLS_TL_GET_COPY flag in order to operate with PKCS11 trust list. In
152 * that case the issuer must be freed using gnutls_x509_crt_init().
154 gnutls_x509_crt_t getIssuer(gnutls_session_t session, gnutls_x509_crt_t cert)
156 gnutls_certificate_credentials_t cred;
157 TPKP_CHECK_THROW_EXCEPTION(
158 gnutls_credentials_get(session, GNUTLS_CRD_CERTIFICATE, (void **)&cred)
160 TPKP_E_INTERNAL, "Failed to get credential on session");
162 gnutls_x509_crt_t issuer;
163 TPKP_CHECK_THROW_EXCEPTION(
164 gnutls_x509_crt_init(&issuer) == GNUTLS_E_SUCCESS,
165 TPKP_E_INTERNAL, "Failed to gnutls_x509_crt_init");
167 TPKP_CHECK_THROW_EXCEPTION(
168 gnutls_certificate_get_issuer(cred, cert, &issuer, 0) == GNUTLS_E_SUCCESS,
170 "Failed to get issuer! It's internal error because verify peer2 success already");
178 int tpkp_gnutls_verify_callback(gnutls_session_t session)
180 tpkp_e res = TPKP::ExceptionSafe([&]{
181 gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
182 if (type != GNUTLS_CRT_X509) {
184 * TODO: what should we do if it's not x509 type cert?
185 * for now, just return 0 which means verification success
187 SLOGW("Certificate type of session isn't X509. skipt for now...");
191 unsigned int status = 0;
192 int res = gnutls_certificate_verify_peers2(session, &status);
193 TPKP_CHECK_THROW_EXCEPTION(res == GNUTLS_E_SUCCESS,
194 TPKP_E_CERT_VERIFICATION_FAILED,
195 "Failed to certificate verify peers2.. res: " << gnutls_strerror(res));
197 TPKP_CHECK_THROW_EXCEPTION(status == 0,
198 TPKP_E_CERT_VERIFICATION_FAILED,
199 "Peer certificate verification failed!! status: " << status);
201 std::string url = g_cache.getUrl();
203 TPKP_CHECK_THROW_EXCEPTION(
206 "No url of found in client cache!!");
208 TPKP::Context ctx(url);
209 if (!ctx.hasPins()) {
210 SLOGI("Skip. No static pin data for url: %s", url.c_str());
214 unsigned int listSize = 0;
215 const gnutls_datum_t *certChain = gnutls_certificate_get_peers(session, &listSize);
216 TPKP_CHECK_THROW_EXCEPTION(certChain != nullptr && listSize != 0,
217 TPKP_E_INVALID_PEER_CERT_CHAIN,
218 "no certificate from peer!");
220 for (unsigned int i = 0; i < listSize; i++) {
221 auto crtPtr = d2iCert(certChain++);
224 TPKP::HashAlgo::SHA1,
225 getPubkeyHash(*crtPtr, TPKP::HashAlgo::SHA1));
227 /* add additional root CA cert for last one */
228 if (i == listSize - 1) {
229 auto issuer = getIssuer(session, *crtPtr);
232 TPKP::HashAlgo::SHA1,
233 getPubkeyHash(issuer, TPKP::HashAlgo::SHA1));
237 TPKP_CHECK_THROW_EXCEPTION(ctx.checkPubkeyPins(),
238 TPKP_E_PUBKEY_MISMATCH, "THe pubkey mismatched with pinned data!");
241 return err_tpkp_to_gnutlse(res);
245 tpkp_e tpkp_gnutls_set_url_data(const char *url)
247 return TPKP::ExceptionSafe([&]{
253 void tpkp_gnutls_cleanup(void)
255 tpkp_e res = TPKP::ExceptionSafe([&]{
263 void tpkp_gnutls_cleanup_all(void)
265 g_cache.eraseUrlAll();