]> granicus.if.org Git - p11-kit/commitdiff
Reference implementation of PKCS#11 URIs
authorStef Walter <stefw@collabora.co.uk>
Sat, 19 Feb 2011 15:33:36 +0000 (16:33 +0100)
committerStef Walter <stefw@collabora.co.uk>
Sat, 19 Feb 2011 15:33:36 +0000 (16:33 +0100)
.gitignore
module/Makefile.am
module/p11-kit-proxy.c
module/p11-kit-uri.c [new file with mode: 0644]
module/p11-kit-uri.h [new file with mode: 0644]
module/p11-kit.h
module/util.c [new file with mode: 0644]
module/util.h [new file with mode: 0644]
tests/Makefile.am
tests/uri-test.c [new file with mode: 0644]

index 6788a07a9ec2cd377254f298b06fc39d3919926c..601cff0e664741ba7aaaf9c9bf1add525671969d 100644 (file)
@@ -45,3 +45,4 @@ temp.txt
 /tests/coverage.info
 /tests/hash-test
 /tests/conf-test
+/tests/uri-test
index 49f0a3de95c1c601d59eee03cb0ead6339c0a282..b4339a8ce26e2af0c56a222bd787c9339f60888b 100644 (file)
@@ -4,15 +4,18 @@ incdir = $(includedir)/p11-kit
 
 inc_HEADERS = \
        p11-kit.h \
+       p11-kit-uri.h \
        pkcs11.h
 
 MODULE_SRCS = \
        conf.c conf.h \
        hash.c hash.h \
+       util.c util.h \
        p11-kit-lib.c \
        p11-kit-proxy.c \
        p11-kit-private.h \
        p11-kit-messages.c \
+       p11-kit-uri.c \
        $(inc_HEADERS)
 
 lib_LTLIBRARIES = \
index 02f8ac3ed7dfc1c88cdcb46a154ab5ef9c363d3e..3b43a5f062dcb98ae48ed8179c9d1dbf2d8f273d 100644 (file)
@@ -39,6 +39,7 @@
 #include "pkcs11.h"
 #include "p11-kit.h"
 #include "p11-kit-private.h"
+#include "util.h"
 
 #include <sys/types.h>
 #include <assert.h>
@@ -86,19 +87,6 @@ static struct _Shared {
 #define LIBRARY_VERSION_MAJOR   1
 #define LIBRARY_VERSION_MINOR   1
 
-/* -----------------------------------------------------------------------------
- * UTILITIES
- */
-
-static void*
-xrealloc (void * memory, size_t length)
-{
-       void *allocated = realloc (memory, length);
-       if (!allocated)
-               free (memory);
-       return allocated;
-}
-
 /* -----------------------------------------------------------------------------
  * PKCS#11 PROXY MODULE
  */
diff --git a/module/p11-kit-uri.c b/module/p11-kit-uri.c
new file mode 100644 (file)
index 0000000..8c65301
--- /dev/null
@@ -0,0 +1,886 @@
+/*
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * 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.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * 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 OWNER 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.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+
+#include "pkcs11.h"
+#include "p11-kit-uri.h"
+#include "util.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+enum {
+       CLASS_IDX,
+       LABEL_IDX,
+       ID_IDX,
+       NUM_ATTRS,
+};
+
+struct _P11KitUri {
+       int unrecognized;
+       CK_INFO module;
+       CK_TOKEN_INFO token;
+       CK_ATTRIBUTE attrs[NUM_ATTRS];
+};
+
+const static char HEX_CHARS[] = "0123456789abcdef";
+
+static int
+url_decode (const char *value, const char *end,
+            unsigned char** output, size_t *length)
+{
+       char *a, *b;
+       unsigned char *result, *p;
+
+       assert (output);
+       assert (value <= end);
+
+       /* String can only get shorter */
+       result = malloc ((end - value) + 1);
+       if (!result)
+               return P11_KIT_URI_NO_MEMORY;
+
+       /* Now loop through looking for escapes */
+       p = result;
+       while (value != end) {
+               /*
+                * A percent sign followed by two hex digits means
+                * that the digits represent an escaped character.
+                */
+               if (*value == '%') {
+                       value++;
+                       if (value + 2 > end) {
+                               free (result);
+                               return P11_KIT_URI_BAD_ENCODING;
+                       }
+                       a = strchr (HEX_CHARS, tolower (value[0]));
+                       b = strchr (HEX_CHARS, tolower (value[1]));
+                       if (!a || !b) {
+                               free (result);
+                               return P11_KIT_URI_BAD_ENCODING;
+                       }
+                       *p = (a - HEX_CHARS) << 4;
+                       *(p++) |= (b - HEX_CHARS);
+                       value += 2;
+               } else {
+                       *(p++) = *(value++);
+               }
+       }
+
+       *p = 0;
+       if (length)
+               *length = p - result;
+       *output = result;
+       return P11_KIT_URI_OK;
+}
+
+static char*
+url_encode (const unsigned char *value, const unsigned char *end, size_t *length)
+{
+       char *p;
+       char *result;
+
+       assert (value <= end);
+
+       /* Just allocate for worst case */
+       result = malloc (((end - value) * 3) + 1);
+       if (!result)
+               return NULL;
+
+       /* Now loop through looking for escapes */
+       p = result;
+       while (value != end) {
+
+               /* These characters we let through verbatim */
+               if (isalnum (*value) || strchr ("_-.", *value) != NULL) {
+                       *(p++) = *(value++);
+
+               /* All others get encoded */
+               } else {
+                       *(p++) = '%';
+                       *(p++) = HEX_CHARS[((unsigned char)*value) >> 4];
+                       *(p++) = HEX_CHARS[((unsigned char)*value) & 0x0F];
+                       ++value;
+               }
+       }
+
+       *p = 0;
+       if (length)
+               *length = p - result;
+       return result;
+}
+
+static int
+attribute_to_idx (CK_ATTRIBUTE_TYPE type)
+{
+       switch (type) {
+       case CKA_CLASS:
+               return CLASS_IDX;
+       case CKA_LABEL:
+               return LABEL_IDX;
+       case CKA_ID:
+               return ID_IDX;
+       default:
+               return -1;
+       }
+}
+
+static CK_ATTRIBUTE_TYPE
+idx_to_attribute (int idx)
+{
+       switch (idx) {
+       case CLASS_IDX:
+               return CKA_CLASS;
+       case LABEL_IDX:
+               return CKA_LABEL;
+       case ID_IDX:
+               return CKA_ID;
+       default:
+               assert (0);
+       }
+}
+
+static int
+match_struct_string (const unsigned char *inuri, const unsigned char *real,
+                     size_t length)
+{
+       assert (inuri);
+       assert (real);
+       assert (length > 0);
+
+       /* NULL matches anything */
+       if (inuri[0] == 0)
+               return 1;
+
+       return memcmp (inuri, real, length) == 0 ? 1 : 0;
+}
+
+static int
+match_struct_version (CK_VERSION_PTR inuri, CK_VERSION_PTR real)
+{
+       /* This matches anything */
+       if (inuri->major == (CK_BYTE)-1 && inuri->minor == (CK_BYTE)-1)
+               return 1;
+
+       return memcmp (inuri, real, sizeof (CK_VERSION));
+}
+
+CK_INFO_PTR
+p11_kit_uri_get_module_info (P11KitUri *uri)
+{
+       assert (uri);
+       return &uri->module;
+}
+
+int
+p11_kit_uri_match_module_info (P11KitUri *uri, CK_INFO_PTR info)
+{
+       assert (uri);
+       assert (info);
+
+       if (uri->unrecognized)
+               return 0;
+
+       return (match_struct_string (uri->module.libraryDescription,
+                                    info->libraryDescription,
+                                    sizeof (info->libraryDescription)) &&
+               match_struct_string (uri->module.manufacturerID,
+                                    info->manufacturerID,
+                                    sizeof (info->manufacturerID)) &&
+               match_struct_version (&uri->module.libraryVersion,
+                                     &info->libraryVersion));
+}
+
+CK_TOKEN_INFO_PTR
+p11_kit_uri_get_token_info (P11KitUri *uri)
+{
+       assert (uri);
+       return &uri->token;
+}
+
+int
+p11_kit_uri_match_token_info (P11KitUri *uri, CK_TOKEN_INFO_PTR token_info)
+{
+       assert (uri);
+       assert (token_info);
+
+       if (uri->unrecognized)
+               return 0;
+
+       return (match_struct_string (uri->token.label,
+                                    token_info->label,
+                                    sizeof (token_info->label)) &&
+               match_struct_string (uri->token.manufacturerID,
+                                    token_info->manufacturerID,
+                                    sizeof (token_info->manufacturerID)) &&
+               match_struct_string (uri->token.model,
+                                    token_info->model,
+                                    sizeof (token_info->model)) &&
+               match_struct_string (uri->token.serialNumber,
+                                    token_info->serialNumber,
+                                    sizeof (token_info->serialNumber)));
+}
+
+CK_ATTRIBUTE_PTR
+p11_kit_uri_get_attribute (P11KitUri *uri, CK_ATTRIBUTE_TYPE type)
+{
+       int idx;
+
+       assert (uri);
+
+       idx = attribute_to_idx (type);
+       if (idx < 0)
+               return NULL;
+
+       assert (idx < NUM_ATTRS);
+       if (uri->attrs[idx].ulValueLen == (CK_ULONG)-1)
+               return NULL;
+       return &uri->attrs[idx];
+}
+
+int
+p11_kit_uri_set_attribute (P11KitUri *uri, CK_ATTRIBUTE_PTR attr)
+{
+       void *value = NULL;
+       int idx;
+       int ret;
+
+       assert (uri);
+       assert (attr);
+
+       if (attr->pValue && attr->ulValueLen && attr->ulValueLen != (CK_ULONG)-1) {
+               value = malloc (attr->ulValueLen);
+               if (!value)
+                       return P11_KIT_URI_NO_MEMORY;
+               memcpy (value, attr->pValue, attr->ulValueLen);
+       }
+
+       ret = p11_kit_uri_clear_attribute (uri, attr->type);
+       if (ret < 0){
+               free (value);
+               return ret;
+       }
+
+       idx = attribute_to_idx (attr->type);
+       assert (idx >= 0 && idx < NUM_ATTRS);
+
+       memcpy (&uri->attrs[idx], attr, sizeof (CK_ATTRIBUTE));
+       uri->attrs[idx].pValue = value;
+
+       return P11_KIT_URI_OK;
+}
+
+int
+p11_kit_uri_clear_attribute (P11KitUri *uri, CK_ATTRIBUTE_TYPE type)
+{
+       int idx;
+
+       assert (uri);
+
+       idx = attribute_to_idx (type);
+       if (idx < 0)
+               return P11_KIT_URI_NOT_FOUND;
+       assert (idx < NUM_ATTRS);
+
+       free (uri->attrs[idx].pValue);
+       uri->attrs[idx].pValue = NULL;
+       uri->attrs[idx].ulValueLen = (CK_ULONG)-1;
+       return 0;
+}
+
+static int
+match_attributes (CK_ATTRIBUTE_PTR one, CK_ATTRIBUTE_PTR two)
+{
+       assert (one);
+       assert (two);
+
+       if (one->type != two->type)
+               return 0;
+       if (one->ulValueLen != two->ulValueLen)
+               return 0;
+       if (one->pValue == two->pValue)
+               return 1;
+       if (!one->pValue || !two->pValue)
+               return 0;
+       return memcmp (one->pValue, two->pValue, one->ulValueLen) == 0;
+}
+
+int
+p11_kit_uri_match_attributes (P11KitUri *uri, CK_ATTRIBUTE_PTR attrs,
+                              CK_ULONG n_attrs)
+{
+       CK_ULONG j;
+       int i;
+
+       assert (uri);
+       assert (attrs || !n_attrs);
+
+       if (uri->unrecognized)
+               return 0;
+
+       for (i = 0; i < NUM_ATTRS; ++i) {
+               if (uri->attrs[i].ulValueLen == (CK_ULONG)-1)
+                       continue;
+               for (j = 0; j < n_attrs; ++j) {
+                       if (attrs[j].type == uri->attrs[i].type) {
+                               if (!match_attributes (&uri->attrs[i], &attrs[j]))
+                                       return 0;
+                               break;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+void
+p11_kit_uri_set_unrecognized (P11KitUri *uri, int unrecognized)
+{
+       assert (uri);
+       uri->unrecognized = unrecognized;
+}
+
+int
+p11_kit_uri_any_unrecognized (P11KitUri *uri)
+{
+       assert (uri);
+       return uri->unrecognized;
+}
+
+P11KitUri*
+p11_kit_uri_new (void)
+{
+       P11KitUri *uri;
+       int i;
+
+       uri = calloc (1, sizeof (P11KitUri));
+       if (!uri)
+               return NULL;
+
+       /* So that it matches anything */
+       uri->module.libraryVersion.major = (CK_BYTE)-1;
+       uri->module.libraryVersion.minor = (CK_BYTE)-1;
+
+       for (i = 0; i < NUM_ATTRS; ++i) {
+               uri->attrs[i].type = idx_to_attribute (i);
+               uri->attrs[i].ulValueLen = (CK_ULONG)-1;
+       }
+
+       return uri;
+}
+
+static size_t
+space_strlen (const unsigned char *string, size_t max_length)
+{
+       size_t i = max_length - 1;
+
+       assert (string);
+
+       while (i > 0 && string[i] == ' ')
+               --i;
+       return i + 1;
+}
+
+static int
+format_raw_string (char **string, size_t *length, int *is_first,
+                   const char *name, const char *value)
+{
+       size_t namelen;
+       size_t vallen;
+
+       /* Not set */
+       if (!value)
+               return 1;
+
+       namelen = strlen (name);
+       vallen = strlen (value);
+
+       *string = xrealloc (*string, *length + namelen + vallen + 3);
+       if (!*string)
+               return 0;
+
+       if (!*is_first)
+               (*string)[(*length)++] = ';';
+       memcpy ((*string) + *length, name, namelen);
+       *length += namelen;
+       (*string)[(*length)++] = '=';
+       memcpy ((*string) + *length, value, vallen);
+       *length += vallen;
+       (*string)[*length] = 0;
+       *is_first = 0;
+
+       return 1;
+}
+
+
+static int
+format_struct_string (char **string, size_t *length, int *is_first,
+                      const char *name, const unsigned char *value,
+                      size_t value_max)
+{
+       char *encoded;
+       size_t len;
+       int ret;
+
+       /* Not set */
+       if (!value[0])
+               return 1;
+
+       len = space_strlen (value, value_max);
+       encoded = url_encode (value, value + len, NULL);
+       if (!encoded)
+               return 0;
+
+       ret = format_raw_string (string, length, is_first, name, encoded);
+       free (encoded);
+
+       return ret;
+}
+
+static int
+format_attribute_string (char **string, size_t *length, int *is_first,
+                         const char *name, CK_ATTRIBUTE_PTR attr)
+{
+       unsigned char *value;
+       char *encoded;
+       int ret;
+
+       /* Not set */;
+       if (attr->ulValueLen == (CK_ULONG)-1)
+               return 1;
+
+       value = attr->pValue;
+       encoded = url_encode (value, value + attr->ulValueLen, NULL);
+       if (!encoded)
+               return 0;
+
+       ret = format_raw_string (string, length, is_first, name, encoded);
+       free (encoded);
+
+       return ret;
+}
+
+static int
+format_attribute_class (char **string, size_t *length, int *is_first,
+                        const char *name, CK_ATTRIBUTE_PTR attr)
+{
+       CK_OBJECT_CLASS klass;
+       const char *value;
+
+       /* Not set */;
+       if (attr->ulValueLen != sizeof (klass))
+               return 1;
+
+       klass = *((CK_OBJECT_CLASS*)attr->pValue);
+       switch (klass) {
+       case CKO_DATA:
+               value = "data";
+               break;
+       case CKO_SECRET_KEY:
+               value = "secretkey";
+               break;
+       case CKO_CERTIFICATE:
+               value = "cert";
+               break;
+       case CKO_PUBLIC_KEY:
+               value = "public";
+               break;
+       case CKO_PRIVATE_KEY:
+               value = "private";
+               break;
+       }
+
+       return format_raw_string (string, length, is_first, name, value);
+}
+
+static int
+format_struct_version (char **string, size_t *length, int *is_first,
+                       const char *name, CK_VERSION_PTR version)
+{
+       char buffer[64];
+
+       /* Not set */
+       if (version->major == (CK_BYTE)-1 && version->minor == (CK_BYTE)-1)
+               return 1;
+
+       snprintf (buffer, sizeof (buffer), "%d.%d",
+                 (int)version->major, (int)version->minor);
+       return format_raw_string (string, length, is_first, name, buffer);
+}
+
+int
+p11_kit_uri_format (P11KitUri *uri, char **string)
+{
+       char *result = NULL;
+       size_t length = 0;
+       int is_first = 1;
+
+       result = malloc (128);
+       if (!result)
+               return P11_KIT_URI_NO_MEMORY;
+
+       length = P11_KIT_URI_PREFIX_LEN;
+       memcpy (result, P11_KIT_URI_PREFIX, length);
+       result[length] = 0;
+
+       if (!format_struct_string (&result, &length, &is_first, "library-description",
+                                  uri->module.libraryDescription,
+                                  sizeof (uri->module.libraryDescription)) ||
+           !format_struct_string (&result, &length, &is_first, "library-manufacturer",
+                                  uri->module.manufacturerID,
+                                  sizeof (uri->module.manufacturerID)) ||
+           !format_struct_string (&result, &length, &is_first, "model",
+                                  uri->token.model,
+                                  sizeof (uri->token.model)) ||
+           !format_struct_string (&result, &length, &is_first, "manufacturer",
+                                  uri->token.manufacturerID,
+                                  sizeof (uri->token.manufacturerID)) ||
+           !format_struct_string (&result, &length, &is_first, "serial",
+                                  uri->token.serialNumber,
+                                  sizeof (uri->token.serialNumber)) ||
+           !format_struct_string (&result, &length, &is_first, "token",
+                                  uri->token.label,
+                                  sizeof (uri->token.label)) ||
+           !format_struct_version (&result, &length, &is_first, "library-version",
+                                   &uri->module.libraryVersion)) {
+               free (result);
+               return P11_KIT_URI_NO_MEMORY;
+       }
+
+       if (!format_attribute_string (&result, &length, &is_first, "id",
+                                     &uri->attrs[ID_IDX]) ||
+           !format_attribute_string (&result, &length, &is_first, "object",
+                                     &uri->attrs[LABEL_IDX])) {
+               free (result);
+               return P11_KIT_URI_NO_MEMORY;
+       }
+
+       if (!format_attribute_class (&result, &length, &is_first, "objecttype",
+                                    &uri->attrs[CLASS_IDX])) {
+               free (result);
+               return P11_KIT_URI_NO_MEMORY;
+       }
+
+       *string = result;
+       return P11_KIT_URI_OK;
+}
+
+static int
+parse_string_attribute (const char *name, const char *start, const char *end,
+                        P11KitUri *uri)
+{
+       unsigned char *value;
+       size_t length;
+       int idx, ret;
+
+       assert (start <= end);
+
+       if (strcmp ("id", name) == 0)
+               idx = ID_IDX;
+       else if (strcmp ("object", name) == 0)
+               idx = LABEL_IDX;
+       else
+               return 0;
+
+       ret = url_decode (start, end, &value, &length);
+       if (ret < 0)
+               return ret;
+
+       free (uri->attrs[idx].pValue);
+       uri->attrs[idx].pValue = value;
+       uri->attrs[idx].ulValueLen = length;
+       return 1;
+}
+
+static int
+equals_segment (const char *start, const char *end, const char *match)
+{
+       size_t len = strlen (match);
+       assert (start <= end);
+       return (end - start == len) && memcmp (start, match, len) == 0;
+}
+
+static int
+parse_class_attribute (const char *name, const char *start, const char *end,
+                       P11KitUri *uri)
+{
+       CK_OBJECT_CLASS klass = 0;
+       void *value;
+
+       assert (start <= end);
+
+       if (strcmp ("objecttype", name) != 0)
+               return 0;
+
+       if (equals_segment (start, end, "cert"))
+               klass = CKO_CERTIFICATE;
+       else if (equals_segment (start, end, "public"))
+               klass = CKO_PUBLIC_KEY;
+       else if (equals_segment (start, end, "private"))
+               klass = CKO_PRIVATE_KEY;
+       else if (equals_segment (start, end, "secretkey"))
+               klass = CKO_SECRET_KEY;
+       else if (equals_segment (start, end, "data"))
+               klass = CKO_DATA;
+       else {
+               uri->unrecognized = 1;
+               return 1;
+       }
+
+       value = malloc (sizeof (klass));
+       if (value == NULL)
+               return P11_KIT_URI_NO_MEMORY;
+
+       free (uri->attrs[CLASS_IDX].pValue);
+       memcpy (value, &klass, sizeof (klass));
+       uri->attrs[CLASS_IDX].pValue = value;
+       uri->attrs[CLASS_IDX].ulValueLen = sizeof (klass);
+
+       return 1;
+}
+
+static int
+parse_struct_info (unsigned char *where, size_t length, const char *start,
+                   const char *end, P11KitUri *uri)
+{
+       unsigned char *value;
+       size_t value_length;
+       int ret;
+
+       assert (start <= end);
+
+       ret = url_decode (start, end, &value, &value_length);
+       if (ret < 0)
+               return ret;
+
+       /* Too long, shouldn't match anything */
+       if (value_length > length) {
+               free (value);
+               uri->unrecognized = 1;
+               return 1;
+       }
+
+       memset (where, ' ', length);
+       memcpy (where, value, value_length);
+
+       free (value);
+       return 1;
+}
+
+static int
+parse_token_info (const char *name, const char *start, const char *end,
+                  P11KitUri *uri)
+{
+       unsigned char *where;
+       size_t length;
+
+       assert (start <= end);
+
+       if (strcmp (name, "model") == 0) {
+               where = uri->token.model;
+               length = sizeof (uri->token.model);
+       } else if (strcmp (name, "manufacturer") == 0) {
+               where = uri->token.manufacturerID;
+               length = sizeof (uri->token.manufacturerID);
+       } else if (strcmp (name, "serial") == 0) {
+               where = uri->token.serialNumber;
+               length = sizeof (uri->token.serialNumber);
+       } else if (strcmp (name, "token") == 0) {
+               where = uri->token.label;
+               length = sizeof (uri->token.label);
+       } else {
+               return 0;
+       }
+
+       return parse_struct_info (where, length, start, end, uri);
+}
+
+static int
+atoin (const char *start, const char *end)
+{
+       int ret = 0;
+       while (start != end) {
+               if (*start < '0' || *start > '9')
+                       return -1;
+               ret *= 10;
+               ret += (*start - '0');
+               ++start;
+       }
+       return ret;
+}
+
+static int
+parse_struct_version (const char *start, const char *end, CK_VERSION_PTR version)
+{
+       const char *dot;
+       int val;
+
+       assert (start <= end);
+
+       dot = memchr (start, '.', end - start);
+       if (!dot)
+               dot = end;
+
+       if (dot == start)
+               return P11_KIT_URI_BAD_VERSION;
+       val = atoin (start, dot);
+       if (val < 0 || val >= 255)
+               return P11_KIT_URI_BAD_VERSION;
+       version->major = (CK_BYTE)val;
+       version->minor = 0;
+
+       if (dot != end) {
+               if (dot + 1 == end)
+                       return P11_KIT_URI_BAD_VERSION;
+               val = atoin (dot + 1, end);
+               if (val < 0 || val >= 255)
+                       return P11_KIT_URI_BAD_VERSION;
+               version->minor = (CK_BYTE)val;
+       }
+
+       return 1;
+}
+
+static int
+parse_module_info (const char *name, const char *start, const char *end,
+                   P11KitUri *uri)
+{
+       unsigned char *where;
+       size_t length;
+
+       assert (start <= end);
+
+       if (strcmp (name, "library-description") == 0) {
+               where = uri->module.libraryDescription;
+               length = sizeof (uri->module.libraryDescription);
+       } else if (strcmp (name, "library-manufacturer") == 0) {
+               where = uri->module.manufacturerID;
+               length = sizeof (uri->module.manufacturerID);
+       } else if (strcmp (name, "library-version") == 0) {
+               return parse_struct_version (start, end,
+                                            &uri->module.libraryVersion);
+       } else {
+               return 0;
+       }
+
+       return parse_struct_info (where, length, start, end, uri);
+}
+
+int
+p11_kit_uri_parse (const char *string, P11KitUriContext context,
+                   P11KitUri *uri)
+{
+       const char *spos, *epos;
+       char *key = NULL;
+       int ret = -1;
+       int i;
+
+       assert (string);
+       assert (uri);
+
+       if (strncmp (string, P11_KIT_URI_PREFIX, P11_KIT_URI_PREFIX_LEN) != 0)
+               return P11_KIT_URI_BAD_PREFIX;
+
+       string += P11_KIT_URI_PREFIX_LEN;
+
+       /* Clear everything out */
+       memset (&uri->module, 0, sizeof (uri->module));
+       memset (&uri->token, 0, sizeof (uri->module));
+       for (i = 0; i < NUM_ATTRS; ++i)
+               uri->attrs[i].ulValueLen = (CK_ULONG)-1;
+       uri->module.libraryVersion.major = (CK_BYTE)-1;
+       uri->module.libraryVersion.minor = (CK_BYTE)-1;
+       uri->unrecognized = 0;
+
+       for (;;) {
+               spos = strchr (string, ';');
+               if (spos == NULL) {
+                       spos = string + strlen (string);
+                       assert (*spos == '\0');
+                       if (spos == string)
+                               break;
+               }
+
+               epos = strchr (string, '=');
+               if (epos == NULL || spos == string || epos == string || epos >= spos)
+                       return P11_KIT_URI_BAD_SYNTAX;
+
+               key = malloc ((epos - string) + 1);
+               if (key == NULL)
+                       return P11_KIT_URI_NO_MEMORY;
+               memcpy (key, string, epos - string);
+               key[epos - string] = 0;
+               epos++;
+
+               ret = 0;
+               if (context & P11_KIT_URI_PARSE_OBJECT)
+                       ret = parse_string_attribute (key, epos, spos, uri);
+               if (ret == 0 && context & P11_KIT_URI_PARSE_OBJECT)
+                       ret = parse_class_attribute (key, epos, spos, uri);
+               if (ret == 0 && context & P11_KIT_URI_PARSE_TOKEN)
+                       ret = parse_token_info (key, epos, spos, uri);
+               if (ret == 0 && context & P11_KIT_URI_PARSE_MODULE)
+                       ret = parse_module_info (key, epos, spos, uri);
+               free (key);
+
+               if (ret < 0)
+                       return ret;
+               if (ret == 0)
+                       uri->unrecognized = 1;
+
+               if (*spos == '\0')
+                       break;
+               string = spos + 1;
+       }
+
+       return P11_KIT_URI_OK;
+}
+
+void
+p11_kit_uri_free (P11KitUri *uri)
+{
+       int i;
+
+       if (!uri)
+               return;
+
+       for (i = 0; i < NUM_ATTRS; ++i)
+               free (uri->attrs[i].pValue);
+
+       free (uri);
+}
diff --git a/module/p11-kit-uri.h b/module/p11-kit-uri.h
new file mode 100644 (file)
index 0000000..c5e86bb
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011, Collabora Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * 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.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * 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 OWNER 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.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "pkcs11.h"
+
+#ifndef __P11_KIT_URI_H__
+#define __P11_KIT_URI_H__
+
+#define P11_KIT_URI_PREFIX "pkcs11:"
+#define P11_KIT_URI_PREFIX_LEN 7
+
+typedef enum {
+       P11_KIT_URI_OK = 0,
+       P11_KIT_URI_NO_MEMORY = -1,
+       P11_KIT_URI_BAD_PREFIX = -2,
+       P11_KIT_URI_BAD_ENCODING = -3,
+       P11_KIT_URI_BAD_SYNTAX = -4,
+       P11_KIT_URI_BAD_VERSION = -5,
+       P11_KIT_URI_NOT_FOUND = -6,
+} P11KitUriParseStatus;
+
+typedef enum {
+       P11_KIT_URI_PARSE_MODULE = (1 << 1),
+       P11_KIT_URI_PARSE_TOKEN =   (1 << 2) | P11_KIT_URI_PARSE_MODULE,
+       P11_KIT_URI_PARSE_OBJECT =  (1 << 3) | P11_KIT_URI_PARSE_TOKEN,
+       P11_KIT_URI_PARSE_ANY =     0xFFFFFFFF,
+} P11KitUriContext;
+
+typedef struct _P11KitUri P11KitUri;
+
+CK_INFO_PTR         p11_kit_uri_get_module_info             (P11KitUri *uri);
+
+int                 p11_kit_uri_match_module_info           (P11KitUri *uri,
+                                                             CK_INFO_PTR info);
+
+CK_TOKEN_INFO_PTR   p11_kit_uri_get_token_info              (P11KitUri *uri);
+
+int                 p11_kit_uri_match_token_info            (P11KitUri *uri,
+                                                             CK_TOKEN_INFO_PTR token_info);
+
+CK_ATTRIBUTE_PTR    p11_kit_uri_get_attribute               (P11KitUri *uri,
+                                                             CK_ATTRIBUTE_TYPE type);
+
+int                 p11_kit_uri_set_attribute               (P11KitUri *uri,
+                                                             CK_ATTRIBUTE_PTR attr);
+
+int                 p11_kit_uri_clear_attribute             (P11KitUri *uri,
+                                                             CK_ATTRIBUTE_TYPE type);
+
+int                 p11_kit_uri_match_attributes            (P11KitUri *uri,
+                                                             CK_ATTRIBUTE_PTR attrs,
+                                                             CK_ULONG n_attrs);
+
+void                p11_kit_uri_set_unrecognized            (P11KitUri *uri,
+                                                             int unrecognized);
+
+int                 p11_kit_uri_any_unrecognized            (P11KitUri *uri);
+
+P11KitUri*          p11_kit_uri_new                         (void);
+
+int                 p11_kit_uri_format                      (P11KitUri *uri,
+                                                             char **string);
+
+int                 p11_kit_uri_parse                       (const char *string,
+                                                             P11KitUriContext context,
+                                                             P11KitUri *uri);
+
+void                p11_kit_uri_free                        (P11KitUri *uri);
+
+#endif /* __P11_KIT_URI_H__ */
index 50b93d8637f766669c535f0257ef399ca6ee2b9f..25af193fabac9c5b203730ad113f700801167d5c 100644 (file)
@@ -35,6 +35,8 @@
 #ifndef __P11_KIT_H__
 #define __P11_KIT_H__
 
+#include "pkcs11.h"
+
 CK_RV                    p11_kit_initialize_registered     (void);
 
 CK_RV                    p11_kit_finalize_registered       (void);
diff --git a/module/util.c b/module/util.c
new file mode 100644 (file)
index 0000000..516e70b
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2011 Collabora Ltd
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * 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.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * 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 OWNER 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.
+ *
+ *
+ * CONTRIBUTORS
+ *  Stef Walter <stef@memberwebs.com>
+ */
+
+#include "config.h"
+
+#include "util.h"
+
+#include <stdlib.h>
+
+void*
+xrealloc (void *memory, size_t length)
+{
+       void *allocated = realloc (memory, length);
+       if (!allocated)
+               free (memory);
+       return allocated;
+}
diff --git a/module/util.h b/module/util.h
new file mode 100644 (file)
index 0000000..73f06ec
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011 Collabora Ltd
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * 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.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * 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 OWNER 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.
+ *
+ *
+ * CONTRIBUTORS
+ *  Stef Walter <stef@memberwebs.com>
+ */
+
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+#include <sys/types.h>
+
+void* xrealloc (void *memory, size_t length);
+
+#endif /* __UTIL_H__ */
index 5c9e0e420f982f71cc466bf3594e98a6d11cb8b3..92dba16a9fc9cae99357d21a0b3e7ace2c0dcde9 100644 (file)
@@ -7,7 +7,8 @@ INCLUDES = \
 
 noinst_PROGRAMS = \
        hash-test \
-       conf-test
+       conf-test \
+       uri-test
 
 hash_test_LDADD = \
        $(top_builddir)/module/libp11-kit-testable.la
@@ -15,6 +16,10 @@ hash_test_LDADD = \
 conf_test_LDADD = \
        $(top_builddir)/module/libp11-kit-testable.la
 
+uri_test_LDADD = \
+       $(top_builddir)/module/libp11-kit-testable.la
+
 check-am:
        ./hash-test
        ./conf-test
+       ./uri-test
\ No newline at end of file
diff --git a/tests/uri-test.c b/tests/uri-test.c
new file mode 100644 (file)
index 0000000..40e301a
--- /dev/null
@@ -0,0 +1,1050 @@
+/*
+ * Copyright (c) 2011, Collabora Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * 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.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * 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 OWNER 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.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+#include "CuTest.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "p11-kit-uri.h"
+
+static int
+is_module_empty (P11KitUri *uri)
+{
+       CK_INFO_PTR info = p11_kit_uri_get_module_info (uri);
+       return (info->libraryDescription[0] == 0 &&
+               info->manufacturerID[0] == 0 &&
+               info->libraryVersion.major == (CK_BYTE)-1 &&
+               info->libraryVersion.minor == (CK_BYTE)-1);
+}
+
+static int
+is_token_empty (P11KitUri *uri)
+{
+       CK_TOKEN_INFO_PTR token = p11_kit_uri_get_token_info (uri);
+       return (token->serialNumber[0] == 0 &&
+               token->manufacturerID[0] == 0 &&
+               token->label[0] == 0 &&
+               token->model[0] == 0);
+}
+
+static int
+are_attributes_empty (P11KitUri *uri)
+{
+       return (p11_kit_uri_get_attribute (uri, CKA_LABEL) == NULL &&
+               p11_kit_uri_get_attribute (uri, CKA_ID) == NULL &&
+               p11_kit_uri_get_attribute (uri, CKA_CLASS) == NULL);
+}
+
+static void
+test_uri_parse (CuTest *tc)
+{
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:", P11_KIT_URI_PARSE_MODULE, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       CuAssertTrue (tc, is_module_empty (uri));
+       CuAssertTrue (tc, is_token_empty (uri));
+       CuAssertTrue (tc, are_attributes_empty (uri));
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_bad_scheme (CuTest *tc)
+{
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("http:\\example.com\test", P11_KIT_URI_PARSE_ANY, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_BAD_PREFIX, ret);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_with_label (CuTest *tc)
+{
+       CK_ATTRIBUTE_PTR attr;
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:object=Test%20Label", P11_KIT_URI_PARSE_ANY, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       CuAssertTrue (tc, is_module_empty (uri));
+       CuAssertTrue (tc, is_token_empty (uri));
+
+       attr = p11_kit_uri_get_attribute (uri, CKA_LABEL);
+       CuAssertPtrNotNull (tc, attr);
+       CuAssertTrue (tc, attr->ulValueLen == strlen ("Test Label"));
+       CuAssertTrue (tc, memcmp (attr->pValue, "Test Label", attr->ulValueLen) == 0);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_with_label_and_klass (CuTest *tc)
+{
+       CK_ATTRIBUTE_PTR attr;
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:object=Test%20Label;objecttype=cert", P11_KIT_URI_PARSE_ANY, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       attr = p11_kit_uri_get_attribute (uri, CKA_LABEL);
+       CuAssertPtrNotNull (tc, attr);
+       CuAssertTrue (tc, attr->ulValueLen == strlen ("Test Label"));
+       CuAssertTrue (tc, memcmp (attr->pValue, "Test Label", attr->ulValueLen) == 0);
+
+       attr = p11_kit_uri_get_attribute (uri, CKA_CLASS);
+       CuAssertPtrNotNull (tc, attr);
+       CuAssertTrue (tc, attr->ulValueLen == sizeof (CK_OBJECT_CLASS));
+       CuAssertTrue (tc, *((CK_OBJECT_CLASS_PTR)attr->pValue) == CKO_CERTIFICATE);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_with_id (CuTest *tc)
+{
+       CK_ATTRIBUTE_PTR attr;
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:id=%54%45%53%54%00", P11_KIT_URI_PARSE_OBJECT, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       /* Note that there's a NULL in the attribute (end) */
+       attr = p11_kit_uri_get_attribute (uri, CKA_ID);
+       CuAssertPtrNotNull (tc, attr);
+       CuAssertTrue (tc, attr->ulValueLen == 5);
+       CuAssertTrue (tc, memcmp (attr->pValue, "TEST", 5) == 0);
+
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_with_bad_string_encoding (CuTest *tc)
+{
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:object=Test%", P11_KIT_URI_PARSE_OBJECT, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_BAD_ENCODING, ret);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_with_bad_hex_encoding (CuTest *tc)
+{
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:object=T%xxest", P11_KIT_URI_PARSE_OBJECT, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_BAD_ENCODING, ret);
+
+       p11_kit_uri_free (uri);
+}
+
+static int
+is_space_string (CK_UTF8CHAR_PTR string, CK_ULONG size, const char *check)
+{
+       size_t i, len = strlen (check);
+       if (len > size)
+               return 0;
+       if (memcmp (string, check, len) != 0)
+               return 0;
+       for (i = len; i < size; ++i)
+               if (string[i] != ' ')
+                       return 0;
+       return 1;
+}
+
+static void
+test_uri_parse_with_token (CuTest *tc)
+{
+       P11KitUri *uri = NULL;
+       CK_TOKEN_INFO_PTR token;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:token=Token%20Label;serial=3333;model=Deluxe;manufacturer=Me",
+                                P11_KIT_URI_PARSE_TOKEN, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       token = p11_kit_uri_get_token_info (uri);
+       CuAssertTrue (tc, is_space_string (token->label, sizeof (token->label), "Token Label"));
+       CuAssertTrue (tc, is_space_string (token->serialNumber, sizeof (token->serialNumber), "3333"));
+       CuAssertTrue (tc, is_space_string (token->model, sizeof (token->model), "Deluxe"));
+       CuAssertTrue (tc, is_space_string (token->manufacturerID, sizeof (token->manufacturerID), "Me"));
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_with_token_bad_encoding (CuTest *tc)
+{
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:token=Token%", P11_KIT_URI_PARSE_TOKEN, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_BAD_ENCODING, ret);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_with_bad_syntax (CuTest *tc)
+{
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:token", P11_KIT_URI_PARSE_ANY, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_BAD_SYNTAX, ret);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_with_library (CuTest *tc)
+{
+       P11KitUri *uri = NULL;
+       CK_INFO_PTR info;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:library-description=The%20Library;library-manufacturer=Me",
+                                P11_KIT_URI_PARSE_MODULE, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       info = p11_kit_uri_get_module_info (uri);
+
+       CuAssertTrue (tc, is_space_string (info->manufacturerID, sizeof (info->manufacturerID), "Me"));
+       CuAssertTrue (tc, is_space_string (info->libraryDescription, sizeof (info->libraryDescription), "The Library"));
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_with_library_bad_encoding (CuTest *tc)
+{
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:library-description=Library%", P11_KIT_URI_PARSE_MODULE, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_BAD_ENCODING, ret);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_build_empty (CuTest *tc)
+{
+       P11KitUri *uri;
+       char *string;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_format (uri, &string);
+       CuAssertStrEquals (tc, "pkcs11:", string);
+       free (string);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+set_space_string (CK_BYTE_PTR buffer, CK_ULONG length, const char *string)
+{
+       size_t len = strlen (string);
+       assert (len <= length);
+       memset (buffer, ' ', length);
+       memcpy (buffer, string, len);
+}
+
+static void
+test_uri_build_with_token_info (CuTest *tc)
+{
+       char *string = NULL;
+       P11KitUri *uri;
+       P11KitUri *check;
+       CK_TOKEN_INFO_PTR token;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       token = p11_kit_uri_get_token_info (uri);
+       set_space_string (token->label, sizeof (token->label), "The Label");
+       set_space_string (token->serialNumber, sizeof (token->serialNumber), "44444");
+       set_space_string (token->manufacturerID, sizeof (token->manufacturerID), "Me");
+       set_space_string (token->model, sizeof (token->model), "Deluxe");
+
+       ret = p11_kit_uri_format (uri, &string);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+       CuAssertPtrNotNull (tc, string);
+
+       check = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, check);
+
+       ret = p11_kit_uri_parse (string, P11_KIT_URI_PARSE_TOKEN, check);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       p11_kit_uri_match_token_info (check, p11_kit_uri_get_token_info (uri));
+
+       p11_kit_uri_free (uri);
+       p11_kit_uri_free (check);
+
+       CuAssertTrue (tc, strstr (string, "token=The%20Label") != NULL);
+       CuAssertTrue (tc, strstr (string, "serial=44444") != NULL);
+       CuAssertTrue (tc, strstr (string, "manufacturer=Me") != NULL);
+       CuAssertTrue (tc, strstr (string, "model=Deluxe") != NULL);
+
+       free (string);
+}
+
+static void
+test_uri_build_with_token_null_info (CuTest *tc)
+{
+       char *string = NULL;
+       P11KitUri *uri;
+       CK_TOKEN_INFO_PTR token;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       token = p11_kit_uri_get_token_info (uri);
+       set_space_string (token->label, sizeof (token->label), "The Label");
+
+       ret = p11_kit_uri_format (uri, &string);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       CuAssertTrue (tc, strstr (string, "token=The%20Label") != NULL);
+       CuAssertTrue (tc, strstr (string, "serial=") == NULL);
+
+       free (string);
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_build_with_token_empty_info (CuTest *tc)
+{
+       char *string = NULL;
+       P11KitUri *uri;
+       CK_TOKEN_INFO_PTR token;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       token = p11_kit_uri_get_token_info (uri);
+       set_space_string (token->label, sizeof (token->label), "");
+       set_space_string (token->serialNumber, sizeof (token->serialNumber), "");
+
+       ret = p11_kit_uri_format (uri, &string);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       CuAssertTrue (tc, strstr (string, "token=") != NULL);
+       CuAssertTrue (tc, strstr (string, "serial=") != NULL);
+
+       free (string);
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_build_with_attributes (CuTest *tc)
+{
+       char *string = NULL;
+       P11KitUri *uri;
+       P11KitUri *check;
+       CK_OBJECT_CLASS klass;
+       CK_ATTRIBUTE_PTR attr;
+       CK_ATTRIBUTE at;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       at.type = CKA_LABEL;
+       at.pValue = "The Label";
+       at.ulValueLen = 9;
+       ret = p11_kit_uri_set_attribute (uri, &at);
+
+       at.type = CKA_ID;
+       at.pValue = "HELLO";
+       at.ulValueLen = 5;
+       ret = p11_kit_uri_set_attribute (uri, &at);
+
+       klass = CKO_DATA;
+       at.type = CKA_CLASS;
+       at.pValue = &klass;
+       at.ulValueLen = sizeof (klass);
+       ret = p11_kit_uri_set_attribute (uri, &at);
+
+       ret = p11_kit_uri_format (uri, &string);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       check = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, check);
+
+       ret = p11_kit_uri_parse (string, P11_KIT_URI_PARSE_ANY, check);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       attr = p11_kit_uri_get_attribute (check, CKA_LABEL);
+       CuAssertPtrNotNull (tc, attr);
+       CuAssertTrue (tc, attr->ulValueLen == 9);
+       CuAssertTrue (tc, memcmp (attr->pValue, "The Label", attr->ulValueLen) == 0);
+
+       attr = p11_kit_uri_get_attribute (check, CKA_CLASS);
+       CuAssertPtrNotNull (tc, attr);
+       CuAssertTrue (tc, attr->ulValueLen == sizeof (klass));
+       CuAssertTrue (tc, *((CK_OBJECT_CLASS_PTR)attr->pValue) == klass);
+
+       attr = p11_kit_uri_get_attribute (check, CKA_ID);
+       CuAssertPtrNotNull (tc, attr);
+       CuAssertTrue (tc, attr->ulValueLen == 5);
+       CuAssertTrue (tc, memcmp (attr->pValue, "HELLO", attr->ulValueLen) == 0);
+
+       p11_kit_uri_free (check);
+
+       CuAssertTrue (tc, strstr (string, "object=The%20Label") != NULL);
+       CuAssertTrue (tc, strstr (string, "objecttype=data") != NULL);
+       CuAssertTrue (tc, strstr (string, "id=HELLO") != NULL);
+
+       free (string);
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_private_key (CuTest *tc)
+{
+       P11KitUri *uri;
+       CK_ATTRIBUTE_PTR attr;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:objecttype=private", P11_KIT_URI_PARSE_OBJECT, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       attr = p11_kit_uri_get_attribute (uri, CKA_CLASS);
+       CuAssertPtrNotNull (tc, attr);
+       CuAssertTrue (tc, attr->ulValueLen == sizeof (CK_OBJECT_CLASS));
+       CuAssertTrue (tc, *((CK_OBJECT_CLASS_PTR)attr->pValue) == CKO_PRIVATE_KEY);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_secret_key (CuTest *tc)
+{
+       P11KitUri *uri;
+       CK_ATTRIBUTE_PTR attr;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:objecttype=secretkey", P11_KIT_URI_PARSE_OBJECT, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       attr = p11_kit_uri_get_attribute (uri, CKA_CLASS);
+       CuAssertPtrNotNull (tc, attr);
+       CuAssertTrue (tc, attr->ulValueLen == sizeof (CK_OBJECT_CLASS));
+       CuAssertTrue (tc, *((CK_OBJECT_CLASS_PTR)attr->pValue) == CKO_SECRET_KEY);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_library_version (CuTest *tc)
+{
+       P11KitUri *uri;
+       CK_INFO_PTR info;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:library-version=2.101", P11_KIT_URI_PARSE_MODULE, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       info = p11_kit_uri_get_module_info (uri);
+       CuAssertIntEquals (tc, 2, info->libraryVersion.major);
+       CuAssertIntEquals (tc, 101, info->libraryVersion.minor);
+
+       ret = p11_kit_uri_parse ("pkcs11:library-version=23", P11_KIT_URI_PARSE_MODULE, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       info = p11_kit_uri_get_module_info (uri);
+       CuAssertIntEquals (tc, 23, info->libraryVersion.major);
+       CuAssertIntEquals (tc, 0, info->libraryVersion.minor);
+
+       ret = p11_kit_uri_parse ("pkcs11:library-version=23.", P11_KIT_URI_PARSE_MODULE, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_BAD_VERSION, ret);
+
+       ret = p11_kit_uri_parse ("pkcs11:library-version=a.a", P11_KIT_URI_PARSE_MODULE, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_BAD_VERSION, ret);
+
+       ret = p11_kit_uri_parse ("pkcs11:library-version=.23", P11_KIT_URI_PARSE_MODULE, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_BAD_VERSION, ret);
+
+       ret = p11_kit_uri_parse ("pkcs11:library-version=1000", P11_KIT_URI_PARSE_MODULE, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_BAD_VERSION, ret);
+
+       ret = p11_kit_uri_parse ("pkcs11:library-version=2.1000", P11_KIT_URI_PARSE_MODULE, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_BAD_VERSION, ret);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_parse_unknown_objecttype (CuTest *tc)
+{
+       P11KitUri *uri;
+       CK_ATTRIBUTE_PTR attr;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:objecttype=unknown", P11_KIT_URI_PARSE_OBJECT, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       attr = p11_kit_uri_get_attribute (uri, CKA_CLASS);
+       CuAssertPtrEquals (tc, NULL, attr);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_unrecognized (CuTest *tc)
+{
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:x-blah=some-value", P11_KIT_URI_PARSE_ANY, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       ret = p11_kit_uri_any_unrecognized (uri);
+       CuAssertIntEquals (tc, 1, ret);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_parse_too_long_is_unrecognized (CuTest *tc)
+{
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:model=a-value-that-is-too-long-for-the-field-that-it-goes-with",
+                                P11_KIT_URI_PARSE_ANY, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       ret = p11_kit_uri_any_unrecognized (uri);
+       CuAssertIntEquals (tc, 1, ret);
+
+       p11_kit_uri_free (uri);
+}
+
+
+
+static void
+test_uri_build_objecttype_cert (CuTest *tc)
+{
+       CK_ATTRIBUTE attr;
+       CK_OBJECT_CLASS klass;
+       P11KitUri *uri;
+       char *string;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       klass = CKO_CERTIFICATE;
+       attr.type = CKA_CLASS;
+       attr.pValue = &klass;
+       attr.ulValueLen = sizeof (klass);
+       p11_kit_uri_set_attribute (uri, &attr);
+
+       ret = p11_kit_uri_format (uri, &string);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+       CuAssertTrue (tc, strstr (string, "objecttype=cert") != NULL);
+
+       p11_kit_uri_free (uri);
+       free (string);
+}
+
+static void
+test_uri_build_objecttype_private (CuTest *tc)
+{
+       CK_ATTRIBUTE attr;
+       CK_OBJECT_CLASS klass;
+       P11KitUri *uri;
+       char *string;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       klass = CKO_PRIVATE_KEY;
+       attr.type = CKA_CLASS;
+       attr.pValue = &klass;
+       attr.ulValueLen = sizeof (klass);
+       p11_kit_uri_set_attribute (uri, &attr);
+
+       ret = p11_kit_uri_format (uri, &string);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+       CuAssertTrue (tc, strstr (string, "objecttype=private") != NULL);
+
+       p11_kit_uri_free (uri);
+       free (string);
+}
+
+static void
+test_uri_build_objecttype_public (CuTest *tc)
+{
+       CK_ATTRIBUTE attr;
+       CK_OBJECT_CLASS klass;
+       P11KitUri *uri;
+       char *string;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       klass = CKO_PUBLIC_KEY;
+       attr.type = CKA_CLASS;
+       attr.pValue = &klass;
+       attr.ulValueLen = sizeof (klass);
+       p11_kit_uri_set_attribute (uri, &attr);
+
+       ret = p11_kit_uri_format (uri, &string);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+       CuAssertTrue (tc, strstr (string, "objecttype=public") != NULL);
+
+       p11_kit_uri_free (uri);
+       free (string);
+}
+
+static void
+test_uri_build_objecttype_secret (CuTest *tc)
+{
+       CK_ATTRIBUTE attr;
+       CK_OBJECT_CLASS klass;
+       P11KitUri *uri;
+       char *string;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       klass = CKO_SECRET_KEY;
+       attr.type = CKA_CLASS;
+       attr.pValue = &klass;
+       attr.ulValueLen = sizeof (klass);
+       p11_kit_uri_set_attribute (uri, &attr);
+
+       ret = p11_kit_uri_format (uri, &string);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+       CuAssertTrue (tc, strstr (string, "objecttype=secretkey") != NULL);
+
+       p11_kit_uri_free (uri);
+       free (string);
+}
+
+static void
+test_uri_build_with_library (CuTest *tc)
+{
+       CK_INFO_PTR info;
+       P11KitUri *uri;
+       char *string;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       info = p11_kit_uri_get_module_info (uri);
+       set_space_string (info->libraryDescription, sizeof (info->libraryDescription), "The Description");
+
+       ret = p11_kit_uri_format (uri, &string);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+       CuAssertTrue (tc, strstr (string, "library-description=The%20Description") != NULL);
+
+       p11_kit_uri_free (uri);
+       free (string);
+}
+
+static void
+test_uri_build_library_version (CuTest *tc)
+{
+       CK_INFO_PTR info;
+       P11KitUri *uri;
+       char *string;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       info = p11_kit_uri_get_module_info (uri);
+       info->libraryVersion.major = 2;
+       info->libraryVersion.minor = 10;
+
+       ret = p11_kit_uri_format (uri, &string);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+       CuAssertTrue (tc, strstr (string, "library-version=2.10") != NULL);
+
+       p11_kit_uri_free (uri);
+       free (string);
+}
+
+static void
+test_uri_get_set_unrecognized (CuTest *tc)
+{
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_any_unrecognized (uri);
+       CuAssertIntEquals (tc, 0, ret);
+
+       p11_kit_uri_set_unrecognized (uri, 1);
+
+       ret = p11_kit_uri_any_unrecognized (uri);
+       CuAssertIntEquals (tc, 1, ret);
+
+       p11_kit_uri_set_unrecognized (uri, 0);
+
+       ret = p11_kit_uri_any_unrecognized (uri);
+       CuAssertIntEquals (tc, 0, ret);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_match_token (CuTest *tc)
+{
+       CK_TOKEN_INFO token;
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:model=Giselle", P11_KIT_URI_PARSE_ANY, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       set_space_string (token.label, sizeof (token.label), "A label");
+       set_space_string (token.model, sizeof (token.model), "Giselle");
+
+       ret = p11_kit_uri_match_token_info (uri, &token);
+       CuAssertIntEquals (tc, 1, ret);
+
+       set_space_string (token.label, sizeof (token.label), "Another label");
+
+       ret = p11_kit_uri_match_token_info (uri, &token);
+       CuAssertIntEquals (tc, 1, ret);
+
+       set_space_string (token.model, sizeof (token.model), "Zoolander");
+
+       ret = p11_kit_uri_match_token_info (uri, &token);
+       CuAssertIntEquals (tc, 0, ret);
+
+       p11_kit_uri_set_unrecognized (uri, 1);
+
+       ret = p11_kit_uri_match_token_info (uri, &token);
+       CuAssertIntEquals (tc, 0, ret);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_match_module (CuTest *tc)
+{
+       CK_INFO info;
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:library-description=Quiet", P11_KIT_URI_PARSE_ANY, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       set_space_string (info.libraryDescription, sizeof (info.libraryDescription), "Quiet");
+       set_space_string (info.manufacturerID, sizeof (info.manufacturerID), "Someone");
+
+       ret = p11_kit_uri_match_module_info (uri, &info);
+       CuAssertIntEquals (tc, 1, ret);
+
+       set_space_string (info.manufacturerID, sizeof (info.manufacturerID), "Someone else");
+
+       ret = p11_kit_uri_match_module_info (uri, &info);
+       CuAssertIntEquals (tc, 1, ret);
+
+       set_space_string (info.libraryDescription, sizeof (info.libraryDescription), "Leise");
+
+       ret = p11_kit_uri_match_module_info (uri, &info);
+       CuAssertIntEquals (tc, 0, ret);
+
+       p11_kit_uri_set_unrecognized (uri, 1);
+
+       ret = p11_kit_uri_match_module_info (uri, &info);
+       CuAssertIntEquals (tc, 0, ret);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_match_attributes (CuTest *tc)
+{
+       CK_ATTRIBUTE attrs[4];
+       CK_OBJECT_CLASS klass;
+       P11KitUri *uri;
+       int ret;
+
+       attrs[0].type = CKA_ID;
+       attrs[0].pValue = "Blah";
+       attrs[0].ulValueLen = 4;
+
+       attrs[1].type = CKA_LABEL;
+       attrs[1].pValue = "Junk";
+       attrs[1].ulValueLen = 4;
+
+       attrs[2].type = CKA_COLOR;
+       attrs[2].pValue = "blue";
+       attrs[2].ulValueLen = 4;
+
+       klass = CKO_DATA;
+       attrs[3].type = CKA_CLASS;
+       attrs[3].pValue = &klass;
+       attrs[3].ulValueLen = sizeof (klass);
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ret = p11_kit_uri_parse ("pkcs11:object=Fancy;id=Blah;objecttype=data", P11_KIT_URI_PARSE_ANY, uri);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       ret = p11_kit_uri_match_attributes (uri, attrs, 4);
+       CuAssertIntEquals (tc, 0, ret);
+
+       attrs[1].pValue = "Fancy";
+       attrs[1].ulValueLen = 5;
+
+       ret = p11_kit_uri_match_attributes (uri, attrs, 4);
+       CuAssertIntEquals (tc, 1, ret);
+
+       p11_kit_uri_clear_attribute (uri, CKA_CLASS);
+
+       ret = p11_kit_uri_match_attributes (uri, attrs, 4);
+       CuAssertIntEquals (tc, 1, ret);
+
+       attrs[2].pValue = "pink";
+
+       ret = p11_kit_uri_match_attributes (uri, attrs, 4);
+       CuAssertIntEquals (tc, 1, ret);
+
+       p11_kit_uri_set_unrecognized (uri, 1);
+
+       ret = p11_kit_uri_match_attributes (uri, attrs, 4);
+       CuAssertIntEquals (tc, 0, ret);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_get_set_attribute (CuTest *tc)
+{
+       CK_ATTRIBUTE attr;
+       CK_ATTRIBUTE_PTR ptr;
+       P11KitUri *uri;
+       int ret;
+
+       uri = p11_kit_uri_new ();
+       CuAssertPtrNotNull (tc, uri);
+
+       ptr = p11_kit_uri_get_attribute (uri, CKA_LABEL);
+       CuAssertPtrEquals (tc, NULL, ptr);
+
+       ret = p11_kit_uri_clear_attribute (uri, CKA_LABEL);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       ret = p11_kit_uri_clear_attribute (uri, CKA_COLOR);
+       CuAssertIntEquals (tc, P11_KIT_URI_NOT_FOUND, ret);
+
+       attr.type = CKA_LABEL;
+       attr.pValue = "Test";
+       attr.ulValueLen = 4;
+
+       ret = p11_kit_uri_set_attribute (uri, &attr);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       attr.type = CKA_COLOR;
+       ret = p11_kit_uri_set_attribute (uri, &attr);
+       CuAssertIntEquals (tc, P11_KIT_URI_NOT_FOUND, ret);
+
+       ptr = p11_kit_uri_get_attribute (uri, CKA_COLOR);
+       CuAssertPtrEquals (tc, NULL, ptr);
+
+       ptr = p11_kit_uri_get_attribute (uri, CKA_LABEL);
+       CuAssertPtrNotNull (tc, ptr);
+
+       CuAssertTrue (tc, ptr->type == CKA_LABEL);
+       CuAssertTrue (tc, ptr->ulValueLen == 4);
+       CuAssertTrue (tc, memcmp (ptr->pValue, "Test", 4) == 0);
+
+       ret = p11_kit_uri_clear_attribute (uri, CKA_LABEL);
+       CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+       ptr = p11_kit_uri_get_attribute (uri, CKA_LABEL);
+       CuAssertPtrEquals (tc, NULL, ptr);
+
+       p11_kit_uri_free (uri);
+}
+
+static void
+test_uri_free_null (CuTest *tc)
+{
+       p11_kit_uri_free (NULL);
+}
+
+int
+main (void)
+{
+       CuString *output = CuStringNew ();
+       CuSuite* suite = CuSuiteNew ();
+       int ret;
+
+       SUITE_ADD_TEST (suite, test_uri_parse);
+       SUITE_ADD_TEST (suite, test_uri_parse_bad_scheme);
+       SUITE_ADD_TEST (suite, test_uri_parse_with_label);
+       SUITE_ADD_TEST (suite, test_uri_parse_with_label_and_klass);
+       SUITE_ADD_TEST (suite, test_uri_parse_with_id);
+       SUITE_ADD_TEST (suite, test_uri_parse_with_bad_string_encoding);
+       SUITE_ADD_TEST (suite, test_uri_parse_with_bad_hex_encoding);
+       SUITE_ADD_TEST (suite, test_uri_parse_with_token);
+       SUITE_ADD_TEST (suite, test_uri_parse_with_token_bad_encoding);
+       SUITE_ADD_TEST (suite, test_uri_parse_with_bad_syntax);
+       SUITE_ADD_TEST (suite, test_uri_parse_with_library);
+       SUITE_ADD_TEST (suite, test_uri_parse_with_library_bad_encoding);
+       SUITE_ADD_TEST (suite, test_uri_build_empty);
+       SUITE_ADD_TEST (suite, test_uri_build_with_token_info);
+       SUITE_ADD_TEST (suite, test_uri_build_with_token_null_info);
+       SUITE_ADD_TEST (suite, test_uri_build_with_token_empty_info);
+       SUITE_ADD_TEST (suite, test_uri_build_with_attributes);
+       SUITE_ADD_TEST (suite, test_uri_parse_private_key);
+       SUITE_ADD_TEST (suite, test_uri_parse_secret_key);
+       SUITE_ADD_TEST (suite, test_uri_parse_library_version);
+       SUITE_ADD_TEST (suite, test_uri_parse_parse_unknown_objecttype);
+       SUITE_ADD_TEST (suite, test_uri_parse_unrecognized);
+       SUITE_ADD_TEST (suite, test_uri_parse_too_long_is_unrecognized);
+       SUITE_ADD_TEST (suite, test_uri_build_objecttype_cert);
+       SUITE_ADD_TEST (suite, test_uri_build_objecttype_private);
+       SUITE_ADD_TEST (suite, test_uri_build_objecttype_public);
+       SUITE_ADD_TEST (suite, test_uri_build_objecttype_secret);
+       SUITE_ADD_TEST (suite, test_uri_build_with_library);
+       SUITE_ADD_TEST (suite, test_uri_build_library_version);
+       SUITE_ADD_TEST (suite, test_uri_get_set_unrecognized);
+       SUITE_ADD_TEST (suite, test_uri_match_token);
+       SUITE_ADD_TEST (suite, test_uri_match_module);
+       SUITE_ADD_TEST (suite, test_uri_match_attributes);
+       SUITE_ADD_TEST (suite, test_uri_get_set_attribute);
+       SUITE_ADD_TEST (suite, test_uri_free_null);
+
+       CuSuiteRun (suite);
+       CuSuiteSummary (suite, output);
+       CuSuiteDetails (suite, output);
+       printf ("%s\n", output->buffer);
+       ret = suite->failCount;
+       CuSuiteDelete (suite);
+       return ret;
+}
+
+#include "CuTest.c"