Release tpkp-gnutls API 93/54093/5
authorKyungwook Tak <k.tak@samsung.com>
Mon, 14 Dec 2015 05:01:14 +0000 (14:01 +0900)
committerKyungwook Tak <k.tak@samsung.com>
Mon, 14 Dec 2015 05:55:31 +0000 (14:55 +0900)
Change-Id: Ib9c80a6ca8b95a53447a890953c5db844ad0aa04
Signed-off-by: Kyungwook Tak <k.tak@samsung.com>
src/common/include/tpkp_error.h
src/gnutls/include/tpkp_gnutls.h
src/gnutls/tpkp_gnutls.cpp
test/gnutls_test.cpp

index a7149e1..cb8776b 100644 (file)
@@ -27,16 +27,17 @@ extern "C" {
 #endif
 
 typedef enum {
-       TPKP_E_NONE                    = 0,
-       TPKP_E_MEMORY                  = -1,
-       TPKP_E_INVALID_URL             = -2,
-       TPKP_E_INVALID_CERT            = -3,
-       TPKP_E_FAILED_GET_PUBKEY_HASH  = -4,
-       TPKP_E_NO_URL_DATA             = -5,
-       TPKP_E_INVALID_PEER_CERT_CHAIN = -6,
-       TPKP_E_PUBKEY_MISMATCH         = -7,
-       TPKP_E_STD_EXCEPTION           = -99,
-       TPKP_E_INTERNAL                = -100
+       TPKP_E_NONE                     = 0,
+       TPKP_E_MEMORY                   = -1,
+       TPKP_E_INVALID_URL              = -2,
+       TPKP_E_INVALID_CERT             = -3,
+       TPKP_E_FAILED_GET_PUBKEY_HASH   = -4,
+       TPKP_E_NO_URL_DATA              = -5,
+       TPKP_E_INVALID_PEER_CERT_CHAIN  = -6,
+       TPKP_E_PUBKEY_MISMATCH          = -7,
+       TPKP_E_CERT_VERIFICATION_FAILED = -8,
+       TPKP_E_STD_EXCEPTION            = -99,
+       TPKP_E_INTERNAL                 = -100
 } tpkp_e;
 
 #ifdef __cplusplus
index fd412d2..506f8b4 100644 (file)
@@ -30,18 +30,56 @@ extern "C" {
 #include <tpkp_error.h>
 
 /*
- *  @brief   gnutls_certificate_verify_function of verifying pubkey pinning
+ *  @brief   gnutls_certificate_verify_function of verifying pubkey pinning.
  *
- *  @remarks set by gnutls_certificate_verify_function().
+ *  @remarks Set by gnutls_certificate_verify_function().
+ *  @remarks tpkp_gnutls_set_url_data() should be called to set url data before.
+ *  @remarks Verify callback should be called in same thread which calls
+ *           tpkp_gnutls_set_url_data().
  *
+ *  @param[in] session  gnutls session of current connection.
  *
+ *  @return return 0 for the handshake to continue, otherwise return non-zero to terminate.
+ *
+ *  @see tpkp_gnutls_set_url_data()
  */
 int tpkp_gnutls_verify_callback(gnutls_session_t session);
 
+/*
+ *  @brief   Sets current url to check pinned info by certificate verify callback.
+ *
+ *  @remarks Url data is saved thread-specifically.
+ *  @remarks tpkp_gnutls_cleanup() should be called before current thread ended or
+ *           tpkp_gnutls_cleanup_all() should be called on thread globally before the
+ *           process ended to use gnutls.
+ *
+ *  @param[in] url  url which is null terminated c string
+ *
+ *  @return #TPKP_E_NONE on success.
+ *
+ *  @see tpkp_gnutls_cleanup()
+ *  @see tpkp_gnutls_cleanup_all()
+ */
 tpkp_e tpkp_gnutls_set_url_data(const char *url);
 
+/*
+ *  @brief   Cleans up memory of current thread.
+ *
+ *  @remarks Only cleans up current thread's specific memory. It should be called inside
+ *           of thread before end.
+ *  @remarks Call beside of gnutls_deinit().
+ *
+ *  @see tpkp_gnutls_set_url_data()
+ */
 void tpkp_gnutls_cleanup(void);
 
+/*
+ *  @brief   Cleans up all memory used by tpkp_gnutls API.
+ *
+ *  @remarks Should be called thread-globally, after all jobs done by worker threads.
+ *
+ *  @see tpkp_gnutls_set_url_data()
+ */
 void tpkp_gnutls_cleanup_all(void);
 
 #ifdef __cplusplus
index b4ef9f4..d9af289 100644 (file)
@@ -39,20 +39,29 @@ std::mutex s_mutex;
 inline int err_tpkp_to_gnutlse(tpkp_e err) noexcept
 {
        switch (err) {
-       case TPKP_E_NONE:                    return GNUTLS_E_SUCCESS;
-       case TPKP_E_MEMORY:                  return GNUTLS_E_MEMORY_ERROR;
-       case TPKP_E_INVALID_URL:             return GNUTLS_E_INVALID_SESSION;
-       case TPKP_E_NO_URL_DATA:             return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
-       case TPKP_E_PUBKEY_MISMATCH:         return GNUTLS_E_CERTIFICATE_KEY_MISMATCH;
+       case TPKP_E_NONE:                     return GNUTLS_E_SUCCESS;
+       case TPKP_E_MEMORY:                   return GNUTLS_E_MEMORY_ERROR;
+       case TPKP_E_INVALID_URL:              return GNUTLS_E_INVALID_SESSION;
+       case TPKP_E_NO_URL_DATA:              return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+       case TPKP_E_PUBKEY_MISMATCH:          return GNUTLS_E_CERTIFICATE_KEY_MISMATCH;
        case TPKP_E_INVALID_CERT:
        case TPKP_E_INVALID_PEER_CERT_CHAIN:
-       case TPKP_E_FAILED_GET_PUBKEY_HASH:  return GNUTLS_E_PK_SIG_VERIFY_FAILED;
+       case TPKP_E_FAILED_GET_PUBKEY_HASH:   return GNUTLS_E_PK_SIG_VERIFY_FAILED;
+       case TPKP_E_CERT_VERIFICATION_FAILED: return GNUTLS_E_CERTIFICATE_ERROR;
        case TPKP_E_STD_EXCEPTION:
        case TPKP_E_INTERNAL:
-       default:                             return GNUTLS_E_INTERNAL_ERROR;
+       default:                              return GNUTLS_E_INTERNAL_ERROR;
        }
 }
 
+using GnutlsX509Ptr = std::unique_ptr<gnutls_x509_crt_t, void(*)(gnutls_x509_crt_t *)>;
+inline GnutlsX509Ptr createGnutlsX509Ptr(void)
+{
+       return GnutlsX509Ptr(new gnutls_x509_crt_t, [](gnutls_x509_crt_t *ptr) {
+               if (!!ptr) gnutls_x509_crt_deinit(*ptr);
+       });
+}
+
 TPKP::RawBuffer getPubkeyHash(gnutls_x509_crt_t cert, TPKP::HashAlgo algo)
 {
        std::unique_ptr<gnutls_pubkey_t, void(*)(gnutls_pubkey_t *)>
@@ -117,6 +126,51 @@ TPKP::RawBuffer getPubkeyHash(gnutls_x509_crt_t cert, TPKP::HashAlgo algo)
        return out;
 }
 
+GnutlsX509Ptr d2iCert(const gnutls_datum_t *datum)
+{
+       auto crtPtr = createGnutlsX509Ptr();
+
+       TPKP_CHECK_THROW_EXCEPTION(
+               gnutls_x509_crt_init(crtPtr.get()) == GNUTLS_E_SUCCESS,
+               TPKP_E_INTERNAL, "Failed to gnutls_x509_crt_init.");
+       TPKP_CHECK_THROW_EXCEPTION(
+               gnutls_x509_crt_import(*crtPtr, datum, GNUTLS_X509_FMT_DER) >= 0,
+               TPKP_E_INTERNAL, "Failed to import DER to gnutls crt");
+
+       return crtPtr;
+}
+
+/*
+ *  Need not to gnutls_x509_crt_deinit for returned value unless GNUTLS_TL_GET_COPY
+ *  flag is used.
+ *  Refer API description of gnutls_certificate_get_issuer.
+ *
+ *  gnutls_certificate_get_issuer will return the issuer of a given certificate.
+ *  As with gnutls_x509_trust_list_get_issuer() this functions requires the
+ *  GNUTLS_TL_GET_COPY flag in order to operate with PKCS11 trust list. In
+ *  that case the issuer must be freed using gnutls_x509_crt_init().
+ */
+gnutls_x509_crt_t getIssuer(gnutls_session_t session, gnutls_x509_crt_t cert)
+{
+       gnutls_certificate_credentials_t cred;
+       TPKP_CHECK_THROW_EXCEPTION(
+               gnutls_credentials_get(session, GNUTLS_CRD_CERTIFICATE, (void **)&cred)
+                       == GNUTLS_E_SUCCESS,
+               TPKP_E_INTERNAL, "Failed to get credential on session");
+
+       gnutls_x509_crt_t issuer;
+       TPKP_CHECK_THROW_EXCEPTION(
+               gnutls_x509_crt_init(&issuer) == GNUTLS_E_SUCCESS,
+               TPKP_E_INTERNAL, "Failed to gnutls_x509_crt_init");
+
+       TPKP_CHECK_THROW_EXCEPTION(
+               gnutls_certificate_get_issuer(cred, cert, &issuer, 0) == GNUTLS_E_SUCCESS,
+               TPKP_E_INTERNAL,
+               "Failed to get issuer! It's internal error because verify peer2 success already");
+
+       return issuer;
+}
+
 }
 
 EXPORT_API
@@ -125,19 +179,23 @@ int tpkp_gnutls_verify_callback(gnutls_session_t session)
        tpkp_e res = TPKP::ExceptionSafe([&]{
                gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
                if (type != GNUTLS_CRT_X509) {
-                       //
-                       // TODO: what should we do if it's not x509 type cert?
-                       // for now, just return 0 which means verification success
-                       //
+                       /*
+                        * TODO: what should we do if it's not x509 type cert?
+                        * for now, just return 0 which means verification success
+                        */
                        SLOGW("Certificate type of session isn't X509. skipt for now...");
                        return;
                }
 
-               unsigned int listSize = 0;
-               const gnutls_datum_t *certChain = gnutls_certificate_get_peers(session, &listSize);
-               TPKP_CHECK_THROW_EXCEPTION(certChain != nullptr && listSize != 0,
-                       TPKP_E_INVALID_PEER_CERT_CHAIN,
-                       "no certificate from peer!");
+               unsigned int status = 0;
+               int res = gnutls_certificate_verify_peers2(session, &status);
+               TPKP_CHECK_THROW_EXCEPTION(res == GNUTLS_E_SUCCESS,
+                       TPKP_E_CERT_VERIFICATION_FAILED,
+                       "Failed to certificate verify peers2.. res: " << gnutls_strerror(res));
+
+               TPKP_CHECK_THROW_EXCEPTION(status == 0,
+                       TPKP_E_CERT_VERIFICATION_FAILED,
+                       "Peer certificate verification failed!! status: " << status);
 
                auto tid = TPKP::getThreadId();
                std::string url;
@@ -160,27 +218,27 @@ int tpkp_gnutls_verify_callback(gnutls_session_t session)
                        return;
                }
 
+               unsigned int listSize = 0;
+               const gnutls_datum_t *certChain = gnutls_certificate_get_peers(session, &listSize);
+               TPKP_CHECK_THROW_EXCEPTION(certChain != nullptr && listSize != 0,
+                       TPKP_E_INVALID_PEER_CERT_CHAIN,
+                       "no certificate from peer!");
+
                for (unsigned int i = 0; i < listSize; i++) {
-                       std::unique_ptr<gnutls_x509_crt_t, void(*)(gnutls_x509_crt_t *)>
-                               crtPtr(new gnutls_x509_crt_t, [](gnutls_x509_crt_t *ptr)->void
-                                       {
-                                               if (ptr != nullptr)
-                                                       gnutls_x509_crt_deinit(*ptr);
-                                       });
-
-                       TPKP_CHECK_THROW_EXCEPTION(
-                               gnutls_x509_crt_init(crtPtr.get()) == GNUTLS_E_SUCCESS,
-                               TPKP_E_INTERNAL,
-                               "Failed to gnutls_x509_crt_init.");
-
-                       TPKP_CHECK_THROW_EXCEPTION(
-                               gnutls_x509_crt_import(*crtPtr, certChain++, GNUTLS_X509_FMT_DER) >= 0,
-                               TPKP_E_INVALID_CERT,
-                               "Failed to import DER cert to gnutls crt");
+                       auto crtPtr = d2iCert(certChain++);
 
                        ctx.addPubkeyHash(
                                TPKP::HashAlgo::SHA1,
                                getPubkeyHash(*crtPtr, TPKP::HashAlgo::SHA1));
+
+                       /* add additional root CA cert for last one */
+                       if (i == listSize - 1) {
+                               auto issuer = getIssuer(session, *crtPtr);
+
+                               ctx.addPubkeyHash(
+                                       TPKP::HashAlgo::SHA1,
+                                       getPubkeyHash(issuer, TPKP::HashAlgo::SHA1));
+                       }
                }
 
                TPKP_CHECK_THROW_EXCEPTION(ctx.checkPubkeyPins(),
index 3c3399f..870eb40 100644 (file)
  * @version     1.0
  * @brief       tpkp_gnutls unit test.
  */
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include <iostream>
+#include <vector>
+#include <string>
+#include <thread>
 
+#include <unistd.h>
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <netdb.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#include <iostream>
-
 #include <gnutls/gnutls.h>
-
 #include <tpkp_gnutls.h>
-
 #include <boost/test/unit_test.hpp>
 
-#define SHA1_LENGTH 20
-
 namespace {
-const std::string targetUrl = "https://WwW.GooGle.cO.Kr";
-
-const char certStr[] =
-       "-----BEGIN CERTIFICATE-----\n"
-       "MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT\n"
-       "MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0\n"
-       "aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw\n"
-       "WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE\n"
-       "AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n"
-       "CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m\n"
-       "OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu\n"
-       "T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c\n"
-       "JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR\n"
-       "Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz\n"
-       "PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm\n"
-       "aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM\n"
-       "TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g\n"
-       "LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO\n"
-       "BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv\n"
-       "dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB\n"
-       "AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL\n"
-       "NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W\n"
-       "b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S\n"
-       "-----END CERTIFICATE-----\n";
-
-const char sha1hash[] = // kSPKIHash_GeoTrustGlobal
-           "\xc0\x7a\x98\x68\x8d\x89\xfb\xab\x05\x64"
-           "\x0c\x11\x7d\xaa\x7d\x65\xb8\xca\xcc\x4e";
-
-int tcp_connect(const char *ip, int port)
-{
-       int sock;
-       struct sockaddr_in server_addr;
-
-       BOOST_REQUIRE_MESSAGE(
-               (sock = socket(PF_INET, SOCK_STREAM, 0)) >= 0,
-               "Cannot create socket!");
-
-       memset(&server_addr, 0x00, sizeof(server_addr));
-       server_addr.sin_family = AF_INET;
-       server_addr.sin_addr.s_addr = inet_addr(ip);
-       server_addr.sin_port = htons(port);
 
-       BOOST_REQUIRE_MESSAGE(
-               (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr))) >= 0,
-               "Cannot connect to sock:" << sock);
+struct DataSet {
+       gnutls_session_t session;
+       gnutls_certificate_credentials_t cred;
+       int sockfd;
+};
 
-       return sock;
-}
+static std::vector<std::string> s_urlList = {
+       "www.google.com",
+       "www.youtube.com",
+       "www.spideroak.com",
+       "www.facebook.com",
+       "www.dropbox.com",
+       "www.twitter.com",
+       "www.hackerrank.com", /* no pinned data exist */
+       "www.algospot.com"    /* no pinned data exist */
+};
 
-void tcp_close(int sd)
+void connectWithUrl(const std::string &url, int &sockfd)
 {
-       close(sd);
-}
+       struct addrinfo *result;
+       struct addrinfo hints;
+       memset(&hints, 0x00, sizeof(struct addrinfo));
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_flags = AI_CANONNAME;
 
-typedef struct _GIOGnuTLSChannel GIOGnuTLSChannel;
+       int s = getaddrinfo(url.c_str(), "https", &hints, &result);
+       BOOST_REQUIRE_MESSAGE(s == 0, "getaddrinfo err code: " << s << " desc: " << gai_strerror(s));
 
-struct _GIOGnuTLSChannel {
-    int fd;
-};
+       struct addrinfo *rp;
+       for (rp = result; rp != nullptr; rp = rp->ai_next) {
+               sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+               if (sockfd == -1)
+                       continue;
 
-gnutls_session_t makeDefaultSession(void)
-{
-       int ret = gnutls_global_init();
-       BOOST_REQUIRE_MESSAGE(
-               ret == GNUTLS_E_SUCCESS,
-               "Failed to gnutls global init: " << gnutls_strerror(ret));
+               if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) != -1) {
+                       char *ipaddr = inet_ntoa(*((struct in_addr *)rp->ai_addr));
+                       break;
+               }
 
-       gnutls_session_t session;
-       ret = gnutls_init(&session, GNUTLS_CLIENT);
-       BOOST_REQUIRE_MESSAGE(
-               ret == GNUTLS_E_SUCCESS,
-               "Failed to gnutls init session: " << gnutls_strerror(ret));
+               close(sockfd);
+       }
 
-       ret = gnutls_set_default_priority(session);
-       BOOST_REQUIRE_MESSAGE(
-               ret == GNUTLS_E_SUCCESS,
-               "Failed to set default priority on session: " << gnutls_strerror(ret));
+       BOOST_REQUIRE_MESSAGE(rp != nullptr, "Could not connect on url: " << url);
 
-       return session;
+       std::cout << "url[" << url << "] canonname[" << result->ai_canonname << "] connected!" << std::endl;
+
+       freeaddrinfo(result);
 }
 
 inline gnutls_certificate_credentials_t makeDefaultCred(gnutls_certificate_verify_function *verify_callback)
@@ -135,54 +100,223 @@ inline gnutls_certificate_credentials_t makeDefaultCred(gnutls_certificate_verif
        BOOST_REQUIRE_MESSAGE(
                ret > 0,
                "Failed to gnutls_certificate_set_x509_trust_file ret: " << ret);
-       std::cout << "x509 trust file loaded. cert num: " << ret << std::endl;
 
        gnutls_certificate_set_verify_function(cred, verify_callback);
 
        return cred;
 }
 
-}
+DataSet makeDefaultSession(const std::string &url)
+{
+       DataSet data;
 
-BOOST_AUTO_TEST_SUITE(TPKP_GNUTLS_TEST)
+       data.cred = makeDefaultCred(&tpkp_gnutls_verify_callback);
 
-#define MAX_BUF 1024
-#define MSG "GET / HTTP/1.0\r\n\r\n"
-BOOST_AUTO_TEST_CASE(T00101_positive)
-{
-       gnutls_certificate_credentials_t cred = makeDefaultCred(&tpkp_gnutls_verify_callback);
-       gnutls_session_t session = makeDefaultSession();
+       int ret = gnutls_init(&data.session, GNUTLS_CLIENT);
+       BOOST_REQUIRE_MESSAGE(
+               ret == GNUTLS_E_SUCCESS,
+               "Failed to gnutls init session: " << gnutls_strerror(ret));
 
-       int ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred);
+       ret = gnutls_set_default_priority(data.session);
+       BOOST_REQUIRE_MESSAGE(
+               ret == GNUTLS_E_SUCCESS,
+               "Failed to set default priority on session: " << gnutls_strerror(ret));
+
+       ret = gnutls_credentials_set(data.session, GNUTLS_CRD_CERTIFICATE, data.cred);
        BOOST_REQUIRE_MESSAGE(
                ret == GNUTLS_E_SUCCESS,
                "Failed to gnutls_credentials_set: " << gnutls_strerror(ret));
 
-       int sd = tcp_connect("216.58.221.36", 443);
+       connectWithUrl(url, data.sockfd);
 
        BOOST_REQUIRE_MESSAGE(
-               tpkp_gnutls_set_url_data(targetUrl.c_str()) == TPKP_E_NONE,
+               tpkp_gnutls_set_url_data(url.c_str()) == TPKP_E_NONE,
                "Failed to tpkp_gnutls_set_url_data.");
 
-       gnutls_transport_set_int(session, sd);
-       gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+       gnutls_transport_set_int(data.session, data.sockfd);
+       gnutls_handshake_set_timeout(data.session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+
+       return data;
+}
+
+DataSet makeSessionWithoutPinning(const std::string &url)
+{
+       DataSet data;
 
+       int ret = gnutls_certificate_allocate_credentials(&data.cred);
+       BOOST_REQUIRE_MESSAGE(
+               ret == GNUTLS_E_SUCCESS,
+               "Failed to gnutls_certificate_allocate_credentials: " << gnutls_strerror(ret));
+
+       ret = gnutls_init(&data.session, GNUTLS_CLIENT);
+       BOOST_REQUIRE_MESSAGE(
+               ret == GNUTLS_E_SUCCESS,
+               "Failed to gnutls init session: " << gnutls_strerror(ret));
+
+       ret = gnutls_set_default_priority(data.session);
+       BOOST_REQUIRE_MESSAGE(
+               ret == GNUTLS_E_SUCCESS,
+               "Failed to set default priority on session: " << gnutls_strerror(ret));
+
+       ret = gnutls_credentials_set(data.session, GNUTLS_CRD_CERTIFICATE, data.cred);
+       BOOST_REQUIRE_MESSAGE(
+               ret == GNUTLS_E_SUCCESS,
+               "Failed to gnutls_credentials_set: " << gnutls_strerror(ret));
+
+       connectWithUrl(url, data.sockfd);
+
+       gnutls_transport_set_int(data.session, data.sockfd);
+       gnutls_handshake_set_timeout(data.session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+
+       return data;
+}
+
+DataSet makeDefaultSessionGlobal(const std::string &url)
+{
+       int ret = gnutls_global_init();
+       BOOST_REQUIRE_MESSAGE(
+               ret == GNUTLS_E_SUCCESS,
+               "Failed to gnutls global init: " << gnutls_strerror(ret));
+
+       return makeDefaultSession(url);
+}
+
+void performHandshake(DataSet &data)
+{
+       int ret;
        do {
-               ret = gnutls_handshake(session);
+               ret = gnutls_handshake(data.session);
        } while (ret != GNUTLS_E_SUCCESS && gnutls_error_is_fatal(ret) == 0);
 
        BOOST_REQUIRE_MESSAGE(
                ret == GNUTLS_E_SUCCESS,
-               "Handshake failed: " << gnutls_strerror(ret));
+               "Handshake failed! err code: " << ret << " desc: " << gnutls_strerror(ret));
+}
+
+void cleanup(DataSet &data)
+{
+       gnutls_bye(data.session, GNUTLS_SHUT_RDWR);
+       close(data.sockfd);
+       gnutls_certificate_free_credentials(data.cred);
+       gnutls_deinit(data.session);
 
-       gnutls_bye(session, GNUTLS_SHUT_RDWR);
+       tpkp_gnutls_cleanup();
+}
+
+void cleanupGlobal(DataSet &data)
+{
+       cleanup(data);
+       gnutls_global_deinit();
+}
+
+void perform(const std::string &url)
+{
+       DataSet data = makeDefaultSession(url);
+       performHandshake(data);
+       cleanup(data);
+}
+
+void performWithoutPinning(const std::string &url)
+{
+       DataSet data = makeSessionWithoutPinning(url);
+       performHandshake(data);
+       cleanup(data);
+}
+
+}
+
+BOOST_AUTO_TEST_SUITE(TPKP_GNUTLS_TEST)
+
+BOOST_AUTO_TEST_CASE(T00101_positive_1)
+{
+       gnutls_global_init();
+
+       perform(s_urlList[0]);
+
+       gnutls_global_deinit();
+}
+
+BOOST_AUTO_TEST_CASE(T00102_positive_2)
+{
+       gnutls_global_init();
+
+       perform(s_urlList[1]);
+
+       gnutls_global_deinit();
+}
+
+BOOST_AUTO_TEST_CASE(T00103_positive_3)
+{
+       gnutls_global_init();
 
-    tcp_close(sd);
+       perform(s_urlList[2]);
+
+       gnutls_global_deinit();
+}
+
+BOOST_AUTO_TEST_CASE(T00104_positive_4)
+{
+       gnutls_global_init();
+
+       perform(s_urlList[3]);
+
+       gnutls_global_deinit();
+}
+
+BOOST_AUTO_TEST_CASE(T00105_positive_5)
+{
+       gnutls_global_init();
+
+       perform(s_urlList[4]);
+
+       gnutls_global_deinit();
+}
+
+BOOST_AUTO_TEST_CASE(T00106_positive_6)
+{
+       gnutls_global_init();
+
+       perform(s_urlList[5]);
+
+       gnutls_global_deinit();
+}
+
+BOOST_AUTO_TEST_CASE(T00107_positive_7)
+{
+       gnutls_global_init();
+
+       perform(s_urlList[6]);
+
+       gnutls_global_deinit();
+}
+
+BOOST_AUTO_TEST_CASE(T00108_positive_8)
+{
+       gnutls_global_init();
+
+       perform(s_urlList[7]);
+
+       gnutls_global_deinit();
+}
+
+BOOST_AUTO_TEST_CASE(T00109_positive_all_single_thread)
+{
+       gnutls_global_init();
+
+       for (const auto &url : s_urlList)
+               perform(url);
+
+       gnutls_global_deinit();
+}
+
+BOOST_AUTO_TEST_CASE(T00110_positive_all_single_thread_without_pinning)
+{
+       gnutls_global_init();
 
-    gnutls_certificate_free_credentials(cred);
+       for (const auto &url : s_urlList)
+               performWithoutPinning(url);
 
-    gnutls_deinit(session);
-    gnutls_global_deinit();
+       gnutls_global_deinit();
 }
 
 BOOST_AUTO_TEST_SUITE_END()