tcti-mssim: Replace liburiparser with custom key/value pair parser.

This removes our use of liburiparser with a custom key / value pair
parser. The parser is implemented in the key-value-parse module under
the util directory. The parser is designed to extract a series of key
/ value pairs where each key and value are separated by the '='
character and each pair is separated by the ',' character. The parser
is composed of three parts:

1) The 'parse_key_value' function is used to extract key /value pairs
separated by the '=' character. This function modifies the string by
using the 'strok_r' function to extract the key and value. A reference
to each is then returned to the caller using the key_value_t structure.

2) The 'parse_key_value_string' function is used to extract each key /
value pair parsing the provided string on the ',' character using the
same method as the 'parse_key_value' function. Each string containing a
key / value pair is then passed to the 'parse_key_value' function to
extract the individual components.

3) Finally the 'parse_key_value_string' function is passed a function
pointer conforming to the 'KeyValueFunc' type. This function is invoked
for each key / value pair extracted and is passed a reference to the
key_value_t structure from the previous step. It is also passed a void
pointer to some caller provided data structure which allows the callback
to do something with the key / value pairs. For the purposes of the
mssim TCTI we simply identify valid / invalid keys and store their
values in a data structure.

Once the parser is done extracting data from the provided string the
mssim TCTI will either return an error indicating the configuration
string was invalid, or continue initializing the TCTI based on the
provided data. If a NULL configuration string is provided defaults are
used.

Additionally this commit:
1) Removes the check for liburiparser from the autoconf file.
2) Updates the .travis.yml file to no longer install liburiparser as a
build dependency.
3) Adds unit tests for the functions described above.
4) Updates existing tests and test infrastructure to use configuration
strings in the new format.
5) Updates the man page for the Tss2_Tcti_Mssim_Init fucntion to
describe the new configuration string format.
6) Updates the default configuration string to use 'localhost' instead
of a raw IPv4 address to make the library friendly to platforms that
only support IPv6.

Signed-off-by: Philip Tricca <philip.b.tricca@intel.com>
diff --git a/.travis.yml b/.travis.yml
index 7c5a67e..237a9aa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,7 +25,6 @@
     - autoconf-archive
     - cmake
     - libgcrypt20-dev
-    - liburiparser-dev
     - realpath
     - lcov
     - libssl-dev
diff --git a/Makefile-test.am b/Makefile-test.am
index 7c98647..8298c94 100644
--- a/Makefile-test.am
+++ b/Makefile-test.am
@@ -62,6 +62,7 @@
     test/unit/CopyCommandHeader \
     test/unit/GetNumHandles \
     test/unit/io \
+    test/unit/key-value-parse \
     test/unit/tcti-device \
     test/unit/tcti-mssim \
     test/unit/UINT8-marshal \
@@ -211,6 +212,10 @@
 test_unit_io_LDFLAGS = -Wl,--wrap=connect,--wrap=read,--wrap=socket,--wrap=write
 test_unit_io_SOURCES = test/unit/io.c
 
+test_unit_key_value_parse_CFLAGS  = $(CMOCKA_CFLAGS) $(TESTS_CFLAGS)
+test_unit_key_value_parse_LDADD   = $(CMOCKA_LIBS) $(libutil)
+test_unit_key_value_parse_SOURCES = test/unit/key-value-parse.c
+
 test_unit_CommonPreparePrologue_CFLAGS = $(CMOCKA_CFLAGS) $(TESTS_CFLAGS)
 test_unit_CommonPreparePrologue_LDFLAGS = -Wl,--unresolved-symbols=ignore-all
 test_unit_CommonPreparePrologue_LDADD = $(CMOCKA_LIBS) $(libtss2_sys)
diff --git a/configure.ac b/configure.ac
index 7ec4fde..bf9dd5e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,9 +58,6 @@
                          [AC_MSG_ERROR([cmocka_err])])])
 AM_CONDITIONAL([UNIT], [test "x$enable_unit" != xno])
 
-# Uriparser library required by simulator TCTI library.
-PKG_CHECK_MODULES([URIPARSER],[liburiparser])
-
 AC_ARG_ENABLE([esapi],
             [AS_HELP_STRING([--enable-esapi],
                             [build the esapi layer (default is yes)])],
diff --git a/man/Tss2_Tcti_Mssim_Init.3.in b/man/Tss2_Tcti_Mssim_Init.3.in
index bda75f6..b40b503 100644
--- a/man/Tss2_Tcti_Mssim_Init.3.in
+++ b/man/Tss2_Tcti_Mssim_Init.3.in
@@ -52,16 +52,26 @@
 The
 .I conf
 parameter is a C string used to configure the TCTI context. This
-configuration string is a URI with the IP address and port used to connect
-to an instance of the Microsoft TPM2 simulator. The URI scheme used is the
-non-standard
-.I tcp
-scheme. If a NULL
+configuration string is a series of key / value pairs that specify the host
+and port used to connect to an instance of the Microsoft TPM2 simulator. The
+keys and values are separated by the '=' character, while each key / value
+pair is separated by the ',' character.
+
+The only keys supported in the
 .I conf
-string is provided then the default of
-.I tcp://127.0.0.1:2321/
-is used. If the URI provided omits the port component then the default port
-of 2321 is used.
+string are
+.B host
+and
+.B port.
+The host may be an IPv4 address, an IPv6 address, or a host name. The port
+must be a valid uint16_t in string form. If a NULL
+.I conf
+string is provided by the caller then the default of
+"host=localhost,port=2321" is used. If either
+.B host
+or
+.B port
+are omitted then their respective default value will be used.
 .sp
 Once initialized, the TCTI context returned exposes the Trusted Computing
 Group (TCG) defined API for the lowest level communication with the TPM.
@@ -104,7 +114,7 @@
 TSS2_RC rc;
 TSS2_TCTI_CONTEXT *tcti_context;
 size_t size;
-const char *conf = "tcp://127.0.0.1:2321/"
+const char *conf = "host=localhost,port=2321"
 
 rc = Tss2_Tcti_Mssim_Init (NULL, &size, NULL);
 if (rc != TSS2_RC_SUCCESS) {
diff --git a/src/tss2-esys/esys_tcti_default.c b/src/tss2-esys/esys_tcti_default.c
index bb50120..8e66b5e 100644
--- a/src/tss2-esys/esys_tcti_default.c
+++ b/src/tss2-esys/esys_tcti_default.c
@@ -61,7 +61,7 @@
       .description = "Access to /dev/tpmrm0" },
     { .init = Tss2_Tcti_Device_Init, .conf = "/dev/tpm0",
       .description = "Access to /dev/tpm0" },
-    { .init = Tss2_Tcti_Mssim_Init, .conf = "tcp://127.0.0.1:2321",
+    { .init = Tss2_Tcti_Mssim_Init, .conf = "host=localhost,port=2321",
       .description = "Access to Mssim-simulator for tcp://localhost:2321" },
 };
 
diff --git a/src/tss2-tcti/tcti-mssim.c b/src/tss2-tcti/tcti-mssim.c
index a11abcd..ec0fe04 100644
--- a/src/tss2-tcti/tcti-mssim.c
+++ b/src/tss2-tcti/tcti-mssim.c
@@ -33,19 +33,15 @@
 #include <inttypes.h>
 #include <unistd.h>
 
-#include <uriparser/Uri.h>
-
 #include "tss2_mu.h"
 #include "tss2_tcti_mssim.h"
 
 #include "tcti-mssim.h"
 #include "tcti-common.h"
+#include "util/key-value-parse.h"
 #define LOGMODULE tcti
 #include "util/log.h"
 
-#define TCTI_SOCKET_DEFAULT_CONF "tcp://127.0.0.1:2321"
-#define TCTI_SOCKET_DEFAULT_PORT 2321
-
 /*
  * This function wraps the "up-cast" of the opaque TCTI context type to the
  * type for the mssim TCTI context. The only safeguard we have to ensure this
@@ -453,71 +449,38 @@
     return port;
 }
 /*
- * This function extracts the hostname and port part of the provided conf
- * string (which is really just a URI). The hostname parameter is an output
- * buffer that must be large enough to hold the hostname. HOST_NAME_MAX is
- * probably a good size. The 'port' parameter is an output parameter where
- * we store the port from the URI after we convert it to a uint16.
- * If the URI does not contain a port number then the contents of the 'port'
- * parameter will not be changed.
- * This function returns TSS2_RC_SUCCESS when the 'hostname' and 'port' have
- * been populated successfully. On failure it will return
- * TSS2_TCTI_RC_BAD_VALUE to indicate that the provided conf string contains
- * values that we can't parse or are invalid.
+ * This function is a callback conforming to the KeyValueFunc prototype. It
+ * is called by the key-value-parse module for each key / value pair extracted
+ * from the configuration string. Its sole purpose is to identify valid keys
+ * from the conf string and to store their corresponding values in the
+ * mssim_conf_t structure which is passed through the 'user_data' parameter.
  */
 TSS2_RC
-conf_str_to_host_port (
-    const char *conf,
-    char *hostname,
-    uint16_t *port)
+mssim_kv_callback (const key_value_t *key_value,
+                   void *user_data)
 {
-    UriParserStateA state;
-    UriUriA uri;
-    /* maximum 5 digits in uint16_t + 1 for \0 */
-    char port_str[6] = { 0 };
-    size_t range;
-    TSS2_RC rc = TSS2_RC_SUCCESS;
+    mssim_conf_t *mssim_conf = (mssim_conf_t*)user_data;
 
-    state.uri = &uri;
-    if (uriParseUriA (&state, conf) != URI_SUCCESS) {
-        LOG_WARNING ("Failed to parse provided conf string: %s", conf);
-        rc = TSS2_TCTI_RC_BAD_VALUE;
-        goto out;
+    LOG_TRACE ("key_value: 0x%" PRIxPTR " and user_data: 0x%" PRIxPTR,
+               (uintptr_t)key_value, (uintptr_t)user_data);
+    if (key_value == NULL || user_data == NULL) {
+        LOG_WARNING ("%s passed NULL parameter", __func__);
+        return TSS2_TCTI_RC_GENERAL_FAILURE;
     }
-
-    /* extract host & domain name / fqdn */
-    range = uri.hostText.afterLast - uri.hostText.first;
-    if (range > HOST_NAME_MAX) {
-        LOG_WARNING ("Provided conf string has hostname that exceeds "
-                     "HOST_NAME_MAX.");
-        rc = TSS2_TCTI_RC_BAD_VALUE;
-        goto out;
+    LOG_DEBUG ("key: %s / value: %s\n", key_value->key, key_value->value);
+    if (strcmp (key_value->key, "host") == 0) {
+        mssim_conf->host = key_value->value;
+        return TSS2_RC_SUCCESS;
+    } else if (strcmp (key_value->key, "port") == 0) {
+        mssim_conf->port = string_to_port (key_value->value);
+        if (mssim_conf->port == 0) {
+            return TSS2_TCTI_RC_BAD_VALUE;
+        }
+        return TSS2_RC_SUCCESS;
+    } else {
+        return TSS2_TCTI_RC_BAD_VALUE;
     }
-    strncpy (hostname, uri.hostText.first, range);
-
-    /* extract port number */
-    range = uri.portText.afterLast - uri.portText.first;
-    if (range > 5) {
-        LOG_WARNING ("conf string contains invalid port.");
-        rc = TSS2_TCTI_RC_BAD_VALUE;
-        goto out;
-    } else if (range == 0) {
-        LOG_INFO ("conf string does not contain a port.");
-        goto out;
-    }
-
-    strncpy (port_str, uri.portText.first, range);
-    *port = string_to_port (port_str);
-    if (*port == 0) {
-        LOG_WARNING ("Provided conf string contains invalid port: 0");
-        rc = TSS2_TCTI_RC_BAD_VALUE;
-        goto out;
-    }
-out:
-    uriFreeUriMembersA (&uri);
-    return rc;
 }
-
 void
 tcti_mssim_init_context_data (
     TSS2_TCTI_COMMON_CONTEXT *tcti_common)
@@ -548,12 +511,11 @@
     TSS2_TCTI_MSSIM_CONTEXT *tcti_mssim = (TSS2_TCTI_MSSIM_CONTEXT*)tctiContext;
     TSS2_TCTI_COMMON_CONTEXT *tcti_common = tcti_mssim_down_cast (tcti_mssim);
     TSS2_RC rc;
-    const char *uri_str = conf != NULL ? conf : TCTI_SOCKET_DEFAULT_CONF;
-    char hostname[HOST_NAME_MAX + 1] = { 0 };
-    uint16_t port = TCTI_SOCKET_DEFAULT_PORT;
+    char *conf_copy = NULL;
+    mssim_conf_t mssim_conf = MSSIM_CONF_DEFAULT_INIT;
 
     LOG_TRACE ("tctiContext: 0x%" PRIxPTR ", size: 0x%" PRIxPTR ", conf: %s",
-               (uintptr_t)tctiContext, (uintptr_t)size, uri_str);
+               (uintptr_t)tctiContext, (uintptr_t)size, conf);
     if (size == NULL) {
         return TSS2_TCTI_RC_BAD_VALUE;
     }
@@ -562,17 +524,41 @@
         return TSS2_RC_SUCCESS;
     }
 
-    rc = conf_str_to_host_port (uri_str, hostname, &port);
+    if (conf != NULL) {
+        LOG_TRACE ("conf is not NULL");
+        if (strlen (conf) > TCTI_MSSIM_CONF_MAX) {
+            LOG_WARNING ("Provided conf string exceeds maximum of %u",
+                         TCTI_MSSIM_CONF_MAX);
+            return TSS2_TCTI_RC_BAD_VALUE;
+        }
+        conf_copy = strdup (conf);
+        if (conf_copy == NULL) {
+            LOG_ERROR ("Failed to allocate buffer: %s", strerror (errno));
+            rc = TSS2_TCTI_RC_GENERAL_FAILURE;
+            goto fail_out;
+        }
+        LOG_DEBUG ("Dup'd conf string to: 0x%" PRIxPTR,
+                   (uintptr_t)conf_copy);
+        rc = parse_key_value_string (conf_copy,
+                                     mssim_kv_callback,
+                                     &mssim_conf);
+        if (rc != TSS2_RC_SUCCESS) {
+            goto fail_out;
+        }
+    }
+    LOG_DEBUG ("Initializing mssim TCTI with host: %s, port: %" PRIu16,
+               mssim_conf.host, mssim_conf.port);
+
+    rc = socket_connect (mssim_conf.host,
+                         mssim_conf.port,
+                         &tcti_mssim->tpm_sock);
     if (rc != TSS2_RC_SUCCESS) {
-        return rc;
+        goto fail_out;
     }
 
-    rc = socket_connect (hostname, port, &tcti_mssim->tpm_sock);
-    if (rc != TSS2_RC_SUCCESS) {
-        return rc;
-    }
-
-    rc = socket_connect (hostname, port + 1, &tcti_mssim->platform_sock);
+    rc = socket_connect (mssim_conf.host,
+                         mssim_conf.port + 1,
+                         &tcti_mssim->platform_sock);
     if (rc != TSS2_RC_SUCCESS) {
         goto fail_out;
     }
@@ -583,13 +569,19 @@
         goto fail_out;
     }
 
+    if (conf_copy != NULL) {
+        free (conf_copy);
+    }
     return TSS2_RC_SUCCESS;
 
 fail_out:
+    if (conf_copy != NULL) {
+        free (conf_copy);
+    }
     socket_close (&tcti_mssim->tpm_sock);
     socket_close (&tcti_mssim->platform_sock);
 
-    return TSS2_TCTI_RC_IO_ERROR;
+    return rc;
 }
 
 /* public info structure */
@@ -597,8 +589,7 @@
     .version = TCTI_VERSION,
     .name = "tcti-socket",
     .description = "TCTI module for communication with the Microsoft TPM2 Simulator.",
-    .config_help = "Connection URI in the form tcp://ip_address[:port]. " \
-        "Default is: TCTI_SOCKET_DEFAULT.",
+    .config_help = "Key / value string in the form \"host=localhost,port=2321\".",
     .init = Tss2_Tcti_Mssim_Init,
 };
 
diff --git a/src/tss2-tcti/tcti-mssim.h b/src/tss2-tcti/tcti-mssim.h
index dbb750c..f74fd74 100644
--- a/src/tss2-tcti/tcti-mssim.h
+++ b/src/tss2-tcti/tcti-mssim.h
@@ -28,12 +28,31 @@
 #ifndef TCTI_MSSIM_H
 #define TCTI_MSSIM_H
 
+#include <limits.h>
+
 #include "tcti-common.h"
 #include "util/io.h"
 
+/*
+ * longest possible conf string:
+ * HOST_NAME_MAX + max char uint16 (5) + strlen ("host=,port=") (11)
+ */
+#define TCTI_MSSIM_CONF_MAX (HOST_NAME_MAX + 16)
+#define TCTI_MSSIM_DEFAULT_HOST "localhost"
+#define TCTI_MSSIM_DEFAULT_PORT 2321
+#define MSSIM_CONF_DEFAULT_INIT { \
+    .host = TCTI_MSSIM_DEFAULT_HOST, \
+    .port = TCTI_MSSIM_DEFAULT_PORT, \
+}
+
 #define TCTI_MSSIM_MAGIC 0xf05b04cd9f02728dULL
 
 typedef struct {
+    char *host;
+    uint16_t port;
+} mssim_conf_t;
+
+typedef struct {
     TSS2_TCTI_COMMON_CONTEXT common;
     SOCKET platform_sock;
     SOCKET tpm_sock;
diff --git a/src/util/key-value-parse.c b/src/util/key-value-parse.c
new file mode 100644
index 0000000..597922c
--- /dev/null
+++ b/src/util/key-value-parse.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2018, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "tss2_tpm2_types.h"
+
+#include "util/key-value-parse.h"
+#define LOGMODULE tcti
+#include "util/log.h"
+
+/*
+ * Parse the provided string containing a key / value pair separated by the
+ * '=' character.
+ * NOTE: The 'kv_str' parameter is not 'const' and this function will modify
+ * it as part of the parsing process. The key_value structure will be updated
+ * with references pointing to the appropriate location in the key_value_str
+ * parameter.
+ */
+bool
+parse_key_value (char *key_value_str,
+                 key_value_t *key_value)
+{
+    const char *delim = "=";
+    char *tok, *state;
+
+    LOG_TRACE ("key_value_str: \"%s\" and key_value_t: 0x%" PRIxPTR,
+               key_value_str, (uintptr_t)key_value);
+    if (key_value_str == NULL || key_value == NULL) {
+        LOG_WARNING ("received a NULL parameter, all are required");
+        return false;
+    }
+    tok = strtok_r (key_value_str, delim, &state);
+    if (tok == NULL) {
+        LOG_WARNING ("key / value string is null.");
+        return false;
+    }
+    key_value->key = tok;
+
+    tok = strtok_r (NULL, delim, &state);
+    if (tok == NULL) {
+        LOG_WARNING ("key / value string is invalid");
+        return false;
+    }
+    key_value->value = tok;
+
+    return true;
+}
+/*
+ * This function parses the provided configuration string extracting the
+ * key/value pairs. Each key/value pair extracted is stored in a key_value_t
+ * structure and then passed to the provided callback function for processing.
+ *
+ * NOTE: The 'kv_str' parameter is not 'const' and this function will modify
+ * it as part of the parsing process.
+ */
+TSS2_RC
+parse_key_value_string (char *kv_str,
+                        KeyValueFunc callback,
+                        void *user_data)
+{
+    const char *delim = ",";
+    char *state, *tok;
+    key_value_t key_value = KEY_VALUE_INIT;
+    TSS2_RC rc = TSS2_RC_SUCCESS;
+
+    LOG_TRACE ("kv_str: \"%s\", callback: 0x%" PRIxPTR ", user_data: 0x%"
+               PRIxPTR, kv_str, (uintptr_t)callback,
+               (uintptr_t)user_data);
+    if (kv_str == NULL || callback == NULL || user_data == NULL) {
+        LOG_WARNING ("all parameters are required");
+        return TSS2_TCTI_RC_BAD_VALUE;
+    }
+    for (tok = strtok_r (kv_str, delim, &state);
+         tok;
+         tok = strtok_r (NULL, delim, &state)) {
+        LOG_DEBUG ("parsing key/value: %s", tok);
+        if (parse_key_value (tok, &key_value) != true) {
+            return TSS2_TCTI_RC_BAD_VALUE;
+        }
+        rc = callback (&key_value, user_data);
+        if (rc != TSS2_RC_SUCCESS) {
+            goto out;
+        }
+    }
+out:
+    return rc;
+}
diff --git a/src/util/key-value-parse.h b/src/util/key-value-parse.h
new file mode 100644
index 0000000..7e2e926
--- /dev/null
+++ b/src/util/key-value-parse.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdlib.h>
+
+#include "tss2_tpm2_types.h"
+
+#define KEY_VALUE_INIT { \
+    .key = NULL, \
+    .value = NULL, \
+}
+
+typedef struct {
+    char *key;
+    char *value;
+} key_value_t;
+
+typedef TSS2_RC (*KeyValueFunc) (const key_value_t* key_value,
+                                 void *user_data);
+bool
+parse_key_value (char *key_value_str,
+                 key_value_t *key_value);
+TSS2_RC
+parse_key_value_string (char *kv_str,
+                        KeyValueFunc callback,
+                        void *user_data);
diff --git a/test/integration/context-util.c b/test/integration/context-util.c
index e4a66e5..bf50a8e 100644
--- a/test/integration/context-util.c
+++ b/test/integration/context-util.c
@@ -8,6 +8,7 @@
 #include "tss2_tcti_mssim.h"
 
 #include "context-util.h"
+#include "tss2-tcti/tcti-mssim.h"
 
 /*
  * Initialize a TSS2_TCTI_CONTEXT for the device TCTI.
@@ -49,14 +50,14 @@
  * function. This structure must be freed by the caller.
  */
 TSS2_TCTI_CONTEXT *
-tcti_socket_init(char const *address, uint16_t port)
+tcti_socket_init(char const *host, uint16_t port)
 {
     size_t size;
     TSS2_RC rc;
     TSS2_TCTI_CONTEXT *tcti_ctx;
-    char conf_str[256] = { 0 };
+    char conf_str[TCTI_MSSIM_CONF_MAX] = { 0 };
 
-    snprintf(conf_str, 256, "tcp://%s:%" PRIu16, address, port);
+    snprintf(conf_str, TCTI_MSSIM_CONF_MAX, "host=%s,port=%" PRIu16, host, port);
     rc = Tss2_Tcti_Mssim_Init(NULL, &size, conf_str);
     if (rc != TSS2_RC_SUCCESS) {
         fprintf(stderr, "Faled to get allocation size for tcti context: "
diff --git a/test/unit/esys-default-tcti.c b/test/unit/esys-default-tcti.c
index 84f653b..4d155bb 100644
--- a/test/unit/esys-default-tcti.c
+++ b/test/unit/esys-default-tcti.c
@@ -363,13 +363,13 @@
      */
     expect_value(__wrap_Tss2_Tcti_Mssim_Init, tctiContext, NULL);
     expect_any(__wrap_Tss2_Tcti_Mssim_Init, size);
-    expect_string(__wrap_Tss2_Tcti_Mssim_Init, config, "tcp://127.0.0.1:2321");
+    expect_string(__wrap_Tss2_Tcti_Mssim_Init, config, "host=localhost,port=2321");
     will_return(__wrap_Tss2_Tcti_Mssim_Init, lsize);
     will_return(__wrap_Tss2_Tcti_Mssim_Init, TSS2_RC_SUCCESS);
 
     expect_any(__wrap_Tss2_Tcti_Mssim_Init, tctiContext);
     expect_memory(__wrap_Tss2_Tcti_Mssim_Init, size, &lsize, sizeof(lsize));
-    expect_string(__wrap_Tss2_Tcti_Mssim_Init, config, "tcp://127.0.0.1:2321");
+    expect_string(__wrap_Tss2_Tcti_Mssim_Init, config, "host=localhost,port=2321");
     will_return(__wrap_Tss2_Tcti_Mssim_Init, lsize);
     will_return(__wrap_Tss2_Tcti_Mssim_Init, TSS2_RC_SUCCESS);
 
diff --git a/test/unit/key-value-parse.c b/test/unit/key-value-parse.c
new file mode 100644
index 0000000..cfa7950
--- /dev/null
+++ b/test/unit/key-value-parse.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2018, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "util/key-value-parse.h"
+
+/*
+ * Ensure that a simple key / value string is parsed into its component parts.
+ */
+static void
+parse_key_value_simple_test (void **state)
+{
+    bool ret;
+    char test_str[] = "key=value";
+    key_value_t key_value = KEY_VALUE_INIT;
+
+    ret = parse_key_value (test_str, &key_value);
+    assert_true (ret);
+    assert_string_equal (key_value.key, "key");
+    assert_string_equal (key_value.value, "value");
+}
+/*
+ * Ensure that a NULL key/value string causes parse_key_value to return false.
+ */
+static void
+parse_key_value_NULL_string_test (void **state)
+{
+    bool ret;
+    key_value_t key_value = KEY_VALUE_INIT;
+
+    ret = parse_key_value (NULL, &key_value);
+    assert_false (ret);
+}
+/*
+ * Ensure that a NULL key_value_t parameter causes parse_key_value to return
+ * false.
+ */
+static void
+parse_key_value_NULL_key_value_test (void **state)
+{
+    bool ret;
+    char test_str[] = "key=value";
+
+    ret = parse_key_value (test_str, NULL);
+    assert_false (ret);
+}
+/*
+ * Ensure that an incomplete key/value string with only the "key=" returns
+ * false.
+ */
+static void
+parse_key_value_no_value_test (void **state)
+{
+    bool ret;
+    char test_str[] = "key=";
+    key_value_t key_value = KEY_VALUE_INIT;
+
+    ret = parse_key_value (test_str, &key_value);
+    assert_false (ret);
+}
+/*
+ * Ensure that a key/value string with only the "=value" part returns false.
+ */
+static void
+parse_key_value_no_key_test (void **state)
+{
+    bool ret;
+    char test_str[] = "=value";
+    key_value_t key_value = KEY_VALUE_INIT;
+
+    ret = parse_key_value (test_str, &key_value);
+    assert_false (ret);
+}
+/*
+ * Ensure that a key/value string with the separators in the wrong place
+ * returns false.
+ */
+static void
+parse_key_value_two_seps_test (void **state)
+{
+    bool ret;
+    char test_str[] = "=foo=";
+    key_value_t key_value = KEY_VALUE_INIT;
+
+    ret = parse_key_value (test_str, &key_value);
+    assert_false (ret);
+}
+/*
+ * Ensure that a key/value string with all separators returns false.
+ */
+static void
+parse_key_value_all_seps_test (void **state)
+{
+    bool ret;
+    char test_str[] = "====";
+    key_value_t key_value = KEY_VALUE_INIT;
+
+    ret = parse_key_value (test_str, &key_value);
+    assert_false (ret);
+}
+/*
+ * Ensure that a key/value string that alternates strings and separators
+ * will parse the first two and ignore the rest.
+ */
+static void
+parse_key_value_alt_seps_test (void **state)
+{
+    bool ret;
+    char test_str[] = "key=value=key=value";
+    key_value_t key_value = KEY_VALUE_INIT;
+
+    ret = parse_key_value (test_str, &key_value);
+    assert_true (ret);
+    assert_string_equal (key_value.key, "key");
+    assert_string_equal (key_value.value, "value");
+}
+/*
+ * This is a simple data structure used to hold values parsed from a string
+ * of key/value pairs.
+ */
+#define TEST_DATA_INIT { \
+    .value0 = NULL, \
+    .value1 = NULL, \
+}
+typedef struct {
+    char *value0;
+    char *value1;
+} test_data_t;
+/*
+ * This is a callback function used to handle extracted key / value pairs.
+ */
+TSS2_RC
+key_value_callback (const key_value_t *key_value,
+                    void *user_data)
+{
+    test_data_t *test_data = (test_data_t*)user_data;
+
+    if (strcmp ("key0", key_value->key) == 0) {
+        test_data->value0 = key_value->value;
+        return TSS2_RC_SUCCESS;
+    } else if (strcmp ("key1", key_value->key) == 0) {
+        test_data->value1 = key_value->value;
+        return TSS2_RC_SUCCESS;
+    } else {
+        return 1;
+    }
+}
+/*
+ * This tests the typical case for the parsing of a string of key / value
+ * pairs.
+ */
+static void
+parse_key_value_string_good_test (void **state)
+{
+    TSS2_RC rc;
+    char test_str[] = "key0=value0,key1=value1";
+    test_data_t test_data = TEST_DATA_INIT;
+
+    rc = parse_key_value_string (test_str, key_value_callback, &test_data);
+    assert_int_equal (rc, TSS2_RC_SUCCESS);
+    assert_string_equal (test_data.value0, "value0");
+    assert_string_equal (test_data.value1, "value1");
+}
+/*
+ * This test ensures that he parse_key_value_string function handles a failed
+ * call to parse a key/value pair properly.
+ */
+static void
+parse_key_value_string_no_value_test (void **state)
+{
+    TSS2_RC rc;
+    char test_str[] = "key0=,key1=value1";
+    test_data_t test_data = TEST_DATA_INIT;
+
+    rc = parse_key_value_string (test_str, key_value_callback, &test_data);
+    assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE);
+}
+/*
+ * This test ensures that the parse_key_value_string function handles a failed
+ * call to the user provided callback properly. The return value we get from
+ * the parse_key_value_string function is the same value returned by our
+ * callback.
+ */
+static void
+parse_key_value_string_unknown_key_test (void **state)
+{
+    TSS2_RC rc;
+    char test_str[] = "key0=foo=bar,baz=qux";
+    test_data_t test_data = TEST_DATA_INIT;
+
+    rc = parse_key_value_string (test_str, key_value_callback, &test_data);
+    assert_int_equal (rc, 1);
+}
+/*
+ * The following 3 tests ensures that NULL parameters produce an error.
+ */
+static void
+parse_key_value_string_NULL_kv_string_test (void **state)
+{
+    TSS2_RC rc;
+    test_data_t test_data = TEST_DATA_INIT;
+
+    rc = parse_key_value_string (NULL, key_value_callback, &test_data);
+    assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE);
+}
+static void
+parse_key_value_string_NULL_callback_test (void **state)
+{
+    TSS2_RC rc;
+    char test_str[] = "key0=foo=bar,baz=qux";
+    test_data_t test_data = TEST_DATA_INIT;
+
+    rc = parse_key_value_string (test_str, NULL, &test_data);
+    assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE);
+}
+static void
+parse_key_value_string_NULL_user_data_test (void **state)
+{
+    TSS2_RC rc;
+    char test_str[] = "key0=foo=bar,baz=qux";
+
+    rc = parse_key_value_string (test_str, key_value_callback, NULL);
+    assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE);
+}
+
+int
+main(int argc, char* argv[])
+{
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test (parse_key_value_simple_test),
+        cmocka_unit_test (parse_key_value_NULL_string_test),
+        cmocka_unit_test (parse_key_value_NULL_key_value_test),
+        cmocka_unit_test (parse_key_value_no_value_test),
+        cmocka_unit_test (parse_key_value_no_key_test),
+        cmocka_unit_test (parse_key_value_two_seps_test),
+        cmocka_unit_test (parse_key_value_all_seps_test),
+        cmocka_unit_test (parse_key_value_alt_seps_test),
+        cmocka_unit_test (parse_key_value_string_good_test),
+        cmocka_unit_test (parse_key_value_string_no_value_test),
+        cmocka_unit_test (parse_key_value_string_unknown_key_test),
+        cmocka_unit_test (parse_key_value_string_NULL_kv_string_test),
+        cmocka_unit_test (parse_key_value_string_NULL_callback_test),
+        cmocka_unit_test (parse_key_value_string_NULL_user_data_test),
+    };
+    return cmocka_run_group_tests (tests, NULL, NULL);
+}
diff --git a/test/unit/tcti-mssim.c b/test/unit/tcti-mssim.c
index 56246b9..d6a5dc9 100644
--- a/test/unit/tcti-mssim.c
+++ b/test/unit/tcti-mssim.c
@@ -40,17 +40,15 @@
 
 #include "tss2-tcti/tcti-common.h"
 #include "tss2-tcti/tcti-mssim.h"
+#include "util/key-value-parse.h"
 
 /*
- * This function is implemented in the socket TCTI module but not exposed
- * through the public headers.
+ * This function is defined in the tcti-mssim module but not exposed through
+ * the header.
  */
 TSS2_RC
-conf_str_to_host_port (
-    const char *conf,
-    char *hostname,
-    uint16_t *port);
-
+mssim_kv_callback (const key_value_t *key_value,
+                   void *user_data);
 /*
  * This tests our ability to handle conf strings that have a port
  * component. In this case the 'conf_str_to_host_port' function
@@ -61,14 +59,13 @@
 conf_str_to_host_port_success_test (void **state)
 {
     TSS2_RC rc;
-    char *conf = "tcp://127.0.0.1:2321";
-    char hostname [HOST_NAME_MAX] = { 0 };
-    uint16_t port;
+    char conf[] = "host=127.0.0.1,port=2321";
+    mssim_conf_t mssim_conf = { 0 };
 
-    rc = conf_str_to_host_port (conf, hostname, &port);
+    rc = parse_key_value_string (conf, mssim_kv_callback, &mssim_conf);
     assert_int_equal (rc, TSS2_RC_SUCCESS);
-    assert_int_equal (port, 2321);
-    assert_string_equal (hostname, "127.0.0.1");
+    assert_int_equal (mssim_conf.port, 2321);
+    assert_string_equal (mssim_conf.host, "127.0.0.1");
 }
 
 /*
@@ -82,13 +79,16 @@
 conf_str_to_host_port_no_port_test (void **state)
 {
     TSS2_RC rc;
-    char *conf = "tcp://127.0.0.1";
-    char hostname [HOST_NAME_MAX] = { 0 };
-    uint16_t port = NO_PORT_VALUE;
+    char conf[] = "host=127.0.0.1";
+    mssim_conf_t mssim_conf = {
+        .host = "foo",
+        .port = NO_PORT_VALUE,
+    };
 
-    rc = conf_str_to_host_port (conf, hostname, &port);
+    rc = parse_key_value_string (conf, mssim_kv_callback, &mssim_conf);
     assert_int_equal (rc, TSS2_RC_SUCCESS);
-    assert_int_equal (port, NO_PORT_VALUE);
+    assert_string_equal (mssim_conf.host, "127.0.0.1");
+    assert_int_equal (mssim_conf.port, NO_PORT_VALUE);
 }
 
 /*
@@ -101,14 +101,13 @@
 conf_str_to_host_ipv6_port_success_test (void **state)
 {
     TSS2_RC rc;
-    char *conf = "tcp://[::1]:2321";
-    char hostname [HOST_NAME_MAX] = { 0 };
-    uint16_t port;
+    char conf[] = "host=::1,port=2321";
+    mssim_conf_t mssim_conf = { 0 };
 
-    rc = conf_str_to_host_port (conf, hostname, &port);
+    rc = parse_key_value_string (conf, mssim_kv_callback, &mssim_conf);
     assert_int_equal (rc, TSS2_RC_SUCCESS);
-    assert_int_equal (port, 2321);
-    assert_string_equal (hostname, "::1");
+    assert_int_equal (mssim_conf.port, 2321);
+    assert_string_equal (mssim_conf.host, "::1");
 }
 
 /*
@@ -121,13 +120,13 @@
 conf_str_to_host_ipv6_port_no_port_test (void **state)
 {
     TSS2_RC rc;
-    char *conf = "tcp://[::1]";
-    char hostname [HOST_NAME_MAX] = { 0 };
-    uint16_t port = NO_PORT_VALUE;
+    char conf[] = "host=::1";
+    mssim_conf_t mssim_conf = { .port = NO_PORT_VALUE };
 
-    rc = conf_str_to_host_port (conf, hostname, &port);
+    rc = parse_key_value_string (conf, mssim_kv_callback, &mssim_conf);
     assert_int_equal (rc, TSS2_RC_SUCCESS);
-    assert_int_equal (port, NO_PORT_VALUE);
+    assert_int_equal (mssim_conf.port, NO_PORT_VALUE);
+    assert_string_equal (mssim_conf.host, "::1");
 }
 
 /*
@@ -137,11 +136,10 @@
 conf_str_to_host_port_invalid_port_large_test (void **state)
 {
     TSS2_RC rc;
-    char *conf = "tcp://127.0.0.1:99999";
-    char hostname [HOST_NAME_MAX] = { 0 };
-    uint16_t port;
+    char conf[] = "host=127.0.0.1,port=99999";
+    mssim_conf_t mssim_conf = { 0 };
 
-    rc = conf_str_to_host_port (conf, hostname, &port);
+    rc = parse_key_value_string (conf, mssim_kv_callback, &mssim_conf);
     assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE);
 }
 /* The 'conf_str_to_host_port' function rejects URIs with port == 0 */
@@ -149,11 +147,10 @@
 conf_str_to_host_port_invalid_port_0_test (void **state)
 {
     TSS2_RC rc;
-    char *conf = "tcp://127.0.0.1:0";
-    char hostname [HOST_NAME_MAX] = { 0 };
-    uint16_t port;
+    char conf[] = "host=127.0.0.1,port=0";
+    mssim_conf_t mssim_conf = { 0 };
 
-    rc = conf_str_to_host_port (conf, hostname, &port);
+    rc = parse_key_value_string (conf, mssim_kv_callback, &mssim_conf);
     assert_int_equal (rc, TSS2_TCTI_RC_BAD_VALUE);
 }
 
@@ -269,7 +266,7 @@
 tcti_socket_setup (void **state)
 {
     printf ("%s: before tcti_socket_init_from_conf\n", __func__);
-    *state = tcti_socket_init_from_conf ("tcp://127.0.0.1:666");
+    *state = tcti_socket_init_from_conf ("host=127.0.0.1,port=666");
     printf ("%s: done\n", __func__);
     return 0;
 }