[Cryptech-Commits] [sw/libhal] 01/02: First cut at multi-attribute get/set/delete API.

git at cryptech.is git at cryptech.is
Thu Nov 10 14:58:12 UTC 2016


This is an automated email from the git hooks/post-receive script.

sra at hactrn.net pushed a commit to branch ksng
in repository sw/libhal.

commit 09a065bb67bf055da0417a6c972c11ba5ab13da0
Author: Rob Austein <sra at hactrn.net>
AuthorDate: Tue Nov 8 01:44:50 2016 -0500

    First cut at multi-attribute get/set/delete API.
    
    This is not yet complete, only the ks_volatile driver supports it,
    ks_flash will be a bit more complicated and isn't written yet.
    
    At the moment, this adds a complete duplicate set of
    {set,get,delete}_attributes() functions in parallel to the earlier
    {set,get,delete}_attribute() functions.  We will almost certainly want
    to get rid of the duplicates, probably (but not necessarily) the
    entire single-attribute suite.  At the moment, though, we want both
    sets so we can compare execution speeds of the two sets of functions.
---
 hal.h          |  17 +++++-
 hal_internal.h | 137 ++++++++++++++++++++++++++++++-----------------
 ks_flash.c     |   2 +-
 ks_volatile.c  | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 libhal.py      |  33 ++++++++++--
 rpc_api.c      |  32 ++++++++++-
 rpc_client.c   | 115 ++++++++++++++++++++++++++++++++++++++--
 rpc_pkey.c     |  79 ++++++++++++++++++++++++++-
 rpc_server.c   | 108 +++++++++++++++++++++++++++++++++++++
 unit-tests.py  | 116 +++++++++++++++++++++++++++++-----------
 10 files changed, 698 insertions(+), 106 deletions(-)

diff --git a/hal.h b/hal.h
index 9ac17dc..74e0a51 100644
--- a/hal.h
+++ b/hal.h
@@ -155,6 +155,7 @@
   DEFINE_HAL_ERROR(HAL_ERROR_KSI_INDEX_CHUNK_MISSING,   "Key index chunk missing")                      \
   DEFINE_HAL_ERROR(HAL_ERROR_KSI_INDEX_CHUNK_OVERLAPS,  "Key index chunk overlaps")                     \
   DEFINE_HAL_ERROR(HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE, "Wrong block type in keystore")                 \
+  DEFINE_HAL_ERROR(HAL_ERROR_RPC_PROTOCOL_ERROR,        "RPC protocol error")                           \
   END_OF_HAL_ERROR_LIST
 
 /* Marker to forestall silly line continuation errors */
@@ -783,7 +784,7 @@ extern hal_error_t hal_rpc_pkey_match(const hal_client_handle_t client,
                                       const hal_key_type_t type,
                                       const hal_curve_name_t curve,
                                       const hal_key_flags_t flags,
-                                      hal_rpc_pkey_attribute_t *attributes,
+                                      const hal_rpc_pkey_attribute_t *attributes,
                                       const unsigned attributes_len,
                                       hal_uuid_t *result,
                                       unsigned *result_len,
@@ -804,6 +805,20 @@ extern hal_error_t hal_rpc_pkey_get_attribute(const hal_pkey_handle_t pkey,
 extern hal_error_t hal_rpc_pkey_delete_attribute(const hal_pkey_handle_t pkey,
                                                  const uint32_t type);
 
+extern hal_error_t hal_rpc_pkey_set_attributes(const hal_pkey_handle_t pkey,
+                                               const hal_rpc_pkey_attribute_t *const attributes,
+                                               const unsigned attributes_len);
+
+extern hal_error_t hal_rpc_pkey_get_attributes(const hal_pkey_handle_t pkey,
+                                               hal_rpc_pkey_attribute_t *attributes,
+                                               const unsigned attributes_len,
+                                               uint8_t *attributes_buffer,
+                                               const size_t attributes_buffer_len);
+
+extern hal_error_t hal_rpc_pkey_delete_attributes(const hal_pkey_handle_t pkey,
+                                                  const uint32_t * const types,
+                                                  const unsigned types_len);
+
 extern hal_error_t hal_rpc_client_init(void);
 
 extern hal_error_t hal_rpc_client_close(void);
diff --git a/hal_internal.h b/hal_internal.h
index 5faba54..6b82b9c 100644
--- a/hal_internal.h
+++ b/hal_internal.h
@@ -250,7 +250,7 @@ typedef struct {
                        const hal_key_type_t type,
                        const hal_curve_name_t curve,
                        const hal_key_flags_t flags,
-                       hal_rpc_pkey_attribute_t *attributes,
+                       const hal_rpc_pkey_attribute_t *attributes,
                        const unsigned attributes_len,
                        hal_uuid_t *result,
                        unsigned *result_len,
@@ -271,6 +271,21 @@ typedef struct {
   hal_error_t (*delete_attribute)(const hal_pkey_handle_t pkey,
                                   const uint32_t type);
 
+
+  hal_error_t (*set_attributes)(const hal_pkey_handle_t pkey,
+                                const hal_rpc_pkey_attribute_t *attributes,
+                                const unsigned attributes_len);
+
+  hal_error_t (*get_attributes)(const hal_pkey_handle_t pkey,
+                                hal_rpc_pkey_attribute_t *attributes,
+                                const unsigned attributes_len,
+                                uint8_t *attributes_buffer,
+                                const size_t attributes_buffer_len);
+
+  hal_error_t (*delete_attributes)(const hal_pkey_handle_t pkey,
+                                   const uint32_t *types,
+                                   const unsigned types_len);
+
 } hal_rpc_pkey_dispatch_t;
 
 
@@ -497,7 +512,7 @@ struct hal_ks_driver {
                        const hal_key_type_t type,
                        const hal_curve_name_t curve,
                        const hal_key_flags_t flags,
-                       hal_rpc_pkey_attribute_t *attributes,
+                       const hal_rpc_pkey_attribute_t *attributes,
                        const unsigned attributes_len,
                        hal_uuid_t *result,
                        unsigned *result_len,
@@ -521,6 +536,23 @@ struct hal_ks_driver {
                                   hal_pkey_slot_t *slot,
                                   const uint32_t type);
 
+  hal_error_t (*set_attributes)(hal_ks_t *ks,
+                                hal_pkey_slot_t *slot,
+                                const hal_rpc_pkey_attribute_t *attributes,
+                                const unsigned attributes_len);
+
+  hal_error_t (*get_attributes)(hal_ks_t *ks,
+                                hal_pkey_slot_t *slot,
+                                hal_rpc_pkey_attribute_t *attributes,
+                                const unsigned attributes_len,
+                                uint8_t *attributes_buffer,
+                                const size_t attributes_buffer_len);
+
+  hal_error_t (*delete_attributes)(hal_ks_t *ks,
+                                   hal_pkey_slot_t *slot,
+                                   const uint32_t *types,
+                                   const unsigned types_len);
+
 };
 
 
@@ -622,7 +654,7 @@ static inline hal_error_t hal_ks_match(hal_ks_t *ks,
                                        const hal_key_type_t type,
                                        const hal_curve_name_t curve,
                                        const hal_key_flags_t flags,
-                                       hal_rpc_pkey_attribute_t *attributes,
+                                       const hal_rpc_pkey_attribute_t *attributes,
                                        const unsigned attributes_len,
                                        hal_uuid_t *result,
                                        unsigned *result_len,
@@ -649,11 +681,11 @@ static inline  hal_error_t hal_ks_set_attribute(hal_ks_t *ks,
 }
 
 static inline hal_error_t hal_ks_get_attribute(hal_ks_t *ks,
-                               hal_pkey_slot_t *slot,
-                               const uint32_t type,
-                               uint8_t *value,
-                               size_t *value_len,
-                               const size_t value_max)
+                                               hal_pkey_slot_t *slot,
+                                               const uint32_t type,
+                                               uint8_t *value,
+                                               size_t *value_len,
+                                               const size_t value_max)
 {
   if (ks == NULL || ks->driver == NULL || ks->driver->get_attribute == NULL || slot == NULL)
     return HAL_ERROR_BAD_ARGUMENTS;
@@ -662,8 +694,8 @@ static inline hal_error_t hal_ks_get_attribute(hal_ks_t *ks,
 }
 
 static inline hal_error_t hal_ks_delete_attribute(hal_ks_t *ks,
-                                  hal_pkey_slot_t *slot,
-                                  const uint32_t type)
+                                                  hal_pkey_slot_t *slot,
+                                                  const uint32_t type)
 {
   if (ks == NULL || ks->driver == NULL || ks->driver->delete_attribute == NULL || slot == NULL)
     return HAL_ERROR_BAD_ARGUMENTS;
@@ -671,6 +703,50 @@ static inline hal_error_t hal_ks_delete_attribute(hal_ks_t *ks,
   return ks->driver->delete_attribute(ks, slot, type);
 }
 
+static inline  hal_error_t hal_ks_set_attributes(hal_ks_t *ks,
+                                                 hal_pkey_slot_t *slot,
+                                                 const hal_rpc_pkey_attribute_t *attributes,
+                                                 const unsigned attributes_len)
+{
+  if (ks == NULL || ks->driver == NULL || ks->driver->set_attributes == NULL || slot == NULL ||
+      attributes == NULL || attributes_len == 0)
+    return HAL_ERROR_BAD_ARGUMENTS;
+
+  for (int i = 0; i < attributes_len; i++)
+    if (attributes[i].length == 0 || attributes[i].value == NULL)
+      return HAL_ERROR_BAD_ARGUMENTS;
+
+  return ks->driver->set_attributes(ks, slot, attributes, attributes_len);
+}
+
+static inline hal_error_t hal_ks_get_attributes(hal_ks_t *ks,
+                                                hal_pkey_slot_t *slot,
+                                                hal_rpc_pkey_attribute_t *attributes,
+                                                const unsigned attributes_len,
+                                                uint8_t *attributes_buffer,
+                                                const size_t attributes_buffer_len)
+{
+  if (ks == NULL || ks->driver == NULL || ks->driver->get_attributes == NULL || slot == NULL ||
+      attributes == NULL || attributes_len == 0 ||
+      attributes_buffer == NULL || attributes_buffer_len == 0)
+    return HAL_ERROR_BAD_ARGUMENTS;
+
+  return ks->driver->get_attributes(ks, slot, attributes, attributes_len,
+                                    attributes_buffer, attributes_buffer_len);
+}
+
+static inline hal_error_t hal_ks_delete_attributes(hal_ks_t *ks,
+                                                   hal_pkey_slot_t *slot,
+                                                   const uint32_t *types,
+                                                   const unsigned types_len)
+{
+  if (ks == NULL || ks->driver == NULL || ks->driver->delete_attributes == NULL || slot == NULL ||
+      types == NULL || types_len == 0)
+    return HAL_ERROR_BAD_ARGUMENTS;
+
+  return ks->driver->delete_attributes(ks, slot, types, types_len);
+}
+
 /*
  * Keystore index.  This is intended to be usable by both memory-based
  * (in-memory, mmap(), ...) keystores and keystores based on raw flash.
@@ -803,44 +879,6 @@ extern hal_error_t hal_ks_index_fsck(hal_ks_index_t *ksi);
 
 /*
  * Keystore attribute utilities, for use by keystore drivers.
- *
- * Basic model here is probably to replace the "der" block in a key
- * object with a byte array.  We could use padding to get alignment,
- * but it's probably easier just to do this DNS style, pulling a
- * 16-bit length and 32-bit attribute type out of the byte array
- * directly.  Well, maybe.  I guess if we cast the uint8_t* to a
- * structure pointer we could use the structure to pull out the header
- * fields, but that has portability issues, particulary if the
- * compiler gets tetchy about type punning.
- *
- * Unclear whether we should treat the key DER specially.  Might just
- * give it an attribute code of 0xFFFFFFFF and treat it same as
- * everything else, just always first for convenience.  This assumes
- * that PKCS #11 will never use 0xFFFFFFFF, which is a bit risky, but
- * maybe the code just treats it a little bit specially and knows to
- * skip over the key DER when looking for attributes, etc.
- *
- * We probably don't want to let attributes span block boundaries.  We
- * probably do want to attempt to fit a new attribute into the first
- * available space which can hold it.  In theory, taken together, this
- * means we will only have to update multiple blocks when required to
- * add a new block (in which case the max_blocks count changes).  Most
- * of this only applies to flash, for volatile we can use as much
- * memory as we like, although even there we might want smaller chunks
- * to avoid wasting huge tracts of space that don't end up being used.
- * But maybe that's just a configuration thing for the volatile
- * keystore(s).
- *
- * If we have to rewrite a block at all we might as well compact it,
- * so fragmentation in that sense is a non-issue.  Might need to
- * collapse blocks when deletion has freed up enough space, but that
- * might be something we handle directly in ks_flash rather than in
- * the ks_attribute code.
- *
- * We need some way of figuring out how many attributes there are.
- * Options are a marker (like the IPv4 END-OF-OPTIONS option) or a
- * count in the header.  Count is simpler and lets us pre-allocate
- * arrays so probably go with that.
  */
 
 extern hal_error_t hal_ks_attribute_scan(const uint8_t * const bytes,
@@ -919,6 +957,9 @@ typedef enum {
     RPC_FUNC_PKEY_GET_ATTRIBUTE,
     RPC_FUNC_PKEY_DELETE_ATTRIBUTE,
     RPC_FUNC_PKEY_GET_KEY_CURVE,
+    RPC_FUNC_PKEY_SET_ATTRIBUTES,
+    RPC_FUNC_PKEY_GET_ATTRIBUTES,
+    RPC_FUNC_PKEY_DELETE_ATTRIBUTES,
 } rpc_func_num_t;
 
 #define RPC_VERSION 0x01010000          /* 1.1.0.0 */
diff --git a/ks_flash.c b/ks_flash.c
index 7a11f0f..c96efd5 100644
--- a/ks_flash.c
+++ b/ks_flash.c
@@ -1156,7 +1156,7 @@ static hal_error_t ks_match(hal_ks_t *ks,
                             const hal_key_type_t type,
                             const hal_curve_name_t curve,
                             const hal_key_flags_t flags,
-                            hal_rpc_pkey_attribute_t *attributes,
+                            const hal_rpc_pkey_attribute_t *attributes,
                             const unsigned attributes_len,
                             hal_uuid_t *result,
                             unsigned *result_len,
diff --git a/ks_volatile.c b/ks_volatile.c
index c8a424c..b69c3c0 100644
--- a/ks_volatile.c
+++ b/ks_volatile.c
@@ -390,7 +390,7 @@ static hal_error_t ks_match(hal_ks_t *ks,
                             const hal_key_type_t type,
                             const hal_curve_name_t curve,
                             const hal_key_flags_t flags,
-                            hal_rpc_pkey_attribute_t *attributes,
+                            const hal_rpc_pkey_attribute_t *attributes,
                             const unsigned attributes_len,
                             hal_uuid_t *result,
                             unsigned *result_len,
@@ -447,7 +447,7 @@ static hal_error_t ks_match(hal_ks_t *ks,
                                        key_attrs, k->attributes_len, NULL)) != HAL_OK)
         return err;
 
-      for (hal_rpc_pkey_attribute_t *required = attributes;
+      for (const hal_rpc_pkey_attribute_t *required = attributes;
            ok && required < attributes + attributes_len; required++) {
 
         hal_rpc_pkey_attribute_t *present = key_attrs;
@@ -470,11 +470,11 @@ static hal_error_t ks_match(hal_ks_t *ks,
   return HAL_OK;
 }
 
-static  hal_error_t ks_set_attribute(hal_ks_t *ks,
-                                     hal_pkey_slot_t *slot,
-                                     const uint32_t type,
-                                     const uint8_t * const value,
-                                     const size_t value_len)
+static hal_error_t ks_set_attribute(hal_ks_t *ks,
+                                    hal_pkey_slot_t *slot,
+                                    const uint32_t type,
+                                    const uint8_t * const value,
+                                    const size_t value_len)
 {
   if (ks == NULL || slot == NULL)
     return HAL_ERROR_BAD_ARGUMENTS;
@@ -508,12 +508,12 @@ static  hal_error_t ks_set_attribute(hal_ks_t *ks,
                                  type, value, value_len);
 }
 
-static  hal_error_t ks_get_attribute(hal_ks_t *ks,
-                                     hal_pkey_slot_t *slot,
-                                     const uint32_t type,
-                                     uint8_t *value,
-                                     size_t *value_len,
-                                     const size_t value_max)
+static hal_error_t ks_get_attribute(hal_ks_t *ks,
+                                    hal_pkey_slot_t *slot,
+                                    const uint32_t type,
+                                    uint8_t *value,
+                                    size_t *value_len,
+                                    const size_t value_max)
 {
   if (ks == NULL || slot == NULL)
     return HAL_ERROR_BAD_ARGUMENTS;
@@ -595,6 +595,140 @@ static hal_error_t ks_delete_attribute(hal_ks_t *ks,
   return hal_ks_attribute_delete(bytes, bytes_len, attributes, &k->attributes_len, &total_len, type);
 }
 
+static hal_error_t ks_set_attributes(hal_ks_t *ks,
+                                     hal_pkey_slot_t *slot,
+                                     const hal_rpc_pkey_attribute_t *attributes,
+                                     const unsigned attributes_len)
+{
+  if (ks == NULL || slot == NULL || attributes == NULL || attributes_len == 0)
+    return HAL_ERROR_BAD_ARGUMENTS;
+
+  ks_t *ksv = ks_to_ksv(ks);
+  hal_error_t err;
+  unsigned b;
+
+  if (ksv->db == NULL)
+    return HAL_ERROR_KEYSTORE_ACCESS;
+
+  if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
+    return err;
+
+  ks_key_t * const k = &ksv->db->keys[b];
+
+  if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k))
+    return HAL_ERROR_KEY_NOT_FOUND;
+
+  hal_rpc_pkey_attribute_t attrs[k->attributes_len + attributes_len];
+  uint8_t *bytes = k->der + k->der_len;
+  size_t bytes_len = sizeof(k->der) - k->der_len;
+  size_t total_len;
+
+  if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, k->attributes_len, &total_len)) != HAL_OK)
+    return err;
+
+  for (const hal_rpc_pkey_attribute_t *a = attributes; a < attributes + attributes_len; a++)
+    if ((err =  hal_ks_attribute_insert(bytes, bytes_len, attrs, &k->attributes_len, &total_len,
+                                        a->type, a->value, a->length)) != HAL_OK)
+      return err;
+
+  return HAL_OK;
+}
+
+static hal_error_t ks_get_attributes(hal_ks_t *ks,
+                                     hal_pkey_slot_t *slot,
+                                     hal_rpc_pkey_attribute_t *attributes,
+                                     const unsigned attributes_len,
+                                     uint8_t *attributes_buffer,
+                                     const size_t attributes_buffer_len)
+{
+  if (ks == NULL || slot == NULL || attributes == NULL || attributes_len == 0 ||
+      attributes_buffer == NULL || attributes_buffer_len == 0)
+    return HAL_ERROR_BAD_ARGUMENTS;
+
+  ks_t *ksv = ks_to_ksv(ks);
+  hal_error_t err;
+  unsigned b;
+
+  if (ksv->db == NULL)
+    return HAL_ERROR_KEYSTORE_ACCESS;
+
+  if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
+    return err;
+
+  const ks_key_t * const k = &ksv->db->keys[b];
+
+  if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k))
+    return HAL_ERROR_KEY_NOT_FOUND;
+
+  if (k->attributes_len == 0)
+    return HAL_ERROR_ATTRIBUTE_NOT_FOUND;
+
+  hal_rpc_pkey_attribute_t attrs[k->attributes_len];
+
+  if ((err = hal_ks_attribute_scan(k->der + k->der_len, sizeof(k->der) - k->der_len,
+                                   attrs, k->attributes_len, NULL)) != HAL_OK)
+    return err;
+
+  uint8_t *abuf = attributes_buffer;
+
+  for (int i = 0; i < attributes_len; i++) {
+
+    int j = 0;
+    while (attrs[j].type != attributes[i].type)
+      if (++j >= k->attributes_len)
+        return HAL_ERROR_ATTRIBUTE_NOT_FOUND;
+
+    if (attrs[j].length > attributes_buffer + attributes_buffer_len - abuf)
+      return HAL_ERROR_RESULT_TOO_LONG;
+
+    memcpy(abuf, attrs[j].value, attrs[j].length);
+    attributes[i].value  = abuf;
+    attributes[i].length = attrs[j].length;
+    abuf += attrs[j].length;
+  }
+
+  return HAL_OK;
+}
+
+static hal_error_t ks_delete_attributes(hal_ks_t *ks,
+                                        hal_pkey_slot_t *slot,
+                                        const uint32_t *types,
+                                        const unsigned types_len)
+{
+  if (ks == NULL || slot == NULL || types == NULL || types_len == 0)
+    return HAL_ERROR_BAD_ARGUMENTS;
+
+  ks_t *ksv = ks_to_ksv(ks);
+  hal_error_t err;
+  unsigned b;
+
+  if (ksv->db == NULL)
+    return HAL_ERROR_KEYSTORE_ACCESS;
+
+  if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
+    return err;
+
+  ks_key_t * const k = &ksv->db->keys[b];
+
+  if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k))
+    return HAL_ERROR_KEY_NOT_FOUND;
+
+  hal_rpc_pkey_attribute_t attrs[k->attributes_len + 1];
+  uint8_t *bytes = k->der + k->der_len;
+  size_t bytes_len = sizeof(k->der) - k->der_len;
+  size_t total_len;
+
+  if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, k->attributes_len, &total_len)) != HAL_OK)
+    return err;
+
+  for (int i = 0; i < types_len; i++)
+    if ((err = hal_ks_attribute_delete(bytes, bytes_len, attrs, &k->attributes_len,
+                                       &total_len, types[i])) != HAL_OK)
+      return err;
+
+  return HAL_OK;
+}
+
 const hal_ks_driver_t hal_ks_volatile_driver[1] = {{
   ks_volatile_init,
   ks_volatile_shutdown,
@@ -607,7 +741,10 @@ const hal_ks_driver_t hal_ks_volatile_driver[1] = {{
   ks_match,
   ks_set_attribute,
   ks_get_attribute,
-  ks_delete_attribute
+  ks_delete_attribute,
+  ks_set_attributes,
+  ks_get_attributes,
+  ks_delete_attributes
 }};
 
 #endif /* STATIC_KS_VOLATILE_SLOTS > 0 */
diff --git a/libhal.py b/libhal.py
index 06445f0..4023edc 100644
--- a/libhal.py
+++ b/libhal.py
@@ -112,6 +112,7 @@ HALError.define(HAL_ERROR_KSI_INDEX_CHUNK_ORPHANED  = "Key index chunk orphaned"
 HALError.define(HAL_ERROR_KSI_INDEX_CHUNK_MISSING   = "Key index chunk missing")
 HALError.define(HAL_ERROR_KSI_INDEX_CHUNK_OVERLAPS  = "Key index chunk overlaps")
 HALError.define(HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE = "Wrong block type in keystore")
+HALError.define(HAL_ERROR_RPC_PROTOCOL_ERROR        = "RPC protocol error")
 
 
 class Enum(int):
@@ -183,6 +184,9 @@ RPCFunc.define('''
     RPC_FUNC_PKEY_GET_ATTRIBUTE,
     RPC_FUNC_PKEY_DELETE_ATTRIBUTE,
     RPC_FUNC_PKEY_GET_KEY_CURVE,
+    RPC_FUNC_PKEY_SET_ATTRIBUTES,
+    RPC_FUNC_PKEY_GET_ATTRIBUTES,
+    RPC_FUNC_PKEY_DELETE_ATTRIBUTES,
 ''')
 
 class HALDigestAlgorithm(Enum): pass
@@ -389,6 +393,15 @@ class PKey(Handle):
     def delete_attribute(self, attr_type):
         self.hsm.pkey_delete_attribute(self, attr_type)
 
+    def set_attributes(self, attributes):
+        self.hsm.pkey_set_attributes(self, attributes)
+
+    def get_attributes(self, attributes, attributes_buffer_len = 2048):
+        return self.hsm.pkey_get_attributes(self, attributes, attributes_buffer_len)
+
+    def delete_attributes(self, attributes):
+        self.hsm.pkey_delete_attributes(self, attributes)
+
 
 class HSM(object):
 
@@ -642,7 +655,7 @@ class HSM(object):
                 key_name  = UUID(bytes = r.unpack_bytes())
                 yield key_type, key_curve, key_flags, key_name
 
-    def pkey_match(self, type = 0, curve = 0, flags = 0, attributes = (),
+    def pkey_match(self, type = 0, curve = 0, flags = 0, attributes = {},
                    length = 64, client = 0, session = 0):
         u = UUID(int = 0)
         n = length
@@ -655,8 +668,6 @@ class HSM(object):
                     yield u
 
     def pkey_set_attribute(self, pkey, attr_type, attr_value = None):
-        if attr_value is None and isinstance(attr_type, Attribute):
-            attr_type, attr_value = attr_type.type, attr_type.attr_value
         with self.rpc(RPC_FUNC_PKEY_SET_ATTRIBUTE, pkey, attr_type, attr_value):
             return
 
@@ -667,3 +678,19 @@ class HSM(object):
     def pkey_delete_attribute(self, pkey, attr_type):
         with self.rpc(RPC_FUNC_PKEY_DELETE_ATTRIBUTE, pkey, attr_type):
             return
+
+    def pkey_set_attributes(self, pkey, attributes):
+        with self.rpc(RPC_FUNC_PKEY_SET_ATTRIBUTES, pkey, attributes):
+            return
+
+    def pkey_get_attributes(self, pkey, attributes, attributes_buffer_len = 2048):
+        attributes = tuple(attributes)
+        with self.rpc(RPC_FUNC_PKEY_GET_ATTRIBUTES, pkey, attributes, attributes_buffer_len) as r:
+            n = r.unpack_uint()
+            if n != len(attributes):
+                raise HAL_ERROR_RPC_PROTOCOL_ERROR
+            return dict((r.unpack_uint(), r.unpack_bytes()) for i in xrange(n))
+
+    def pkey_delete_attributes(self, pkey, attributes):
+        with self.rpc(RPC_FUNC_PKEY_DELETE_ATTRIBUTES, pkey, attributes):
+            return
diff --git a/rpc_api.c b/rpc_api.c
index d337eeb..772d522 100644
--- a/rpc_api.c
+++ b/rpc_api.c
@@ -351,7 +351,7 @@ hal_error_t hal_rpc_pkey_match(const hal_client_handle_t client,
                                const hal_key_type_t type,
                                const hal_curve_name_t curve,
                                const hal_key_flags_t flags,
-                               hal_rpc_pkey_attribute_t *attributes,
+                               const hal_rpc_pkey_attribute_t *attributes,
                                const unsigned attributes_len,
                                hal_uuid_t *result,
                                unsigned *result_len,
@@ -399,6 +399,36 @@ hal_error_t hal_rpc_pkey_delete_attribute(const hal_pkey_handle_t pkey,
   return hal_rpc_pkey_dispatch->delete_attribute(pkey, type);
 }
 
+hal_error_t hal_rpc_pkey_set_attributes(const hal_pkey_handle_t pkey,
+                                        const hal_rpc_pkey_attribute_t *attributes,
+                                        const unsigned attributes_len)
+{
+  if (attributes == NULL || attributes_len == 0)
+    return HAL_ERROR_BAD_ARGUMENTS;
+  return hal_rpc_pkey_dispatch->set_attributes(pkey, attributes, attributes_len);
+}
+
+hal_error_t hal_rpc_pkey_get_attributes(const hal_pkey_handle_t pkey,
+                                        hal_rpc_pkey_attribute_t *attributes,
+                                        const unsigned attributes_len,
+                                        uint8_t *attributes_buffer,
+                                        const size_t attributes_buffer_len)
+{
+  if (attributes == NULL || attributes_len == 0)
+    return HAL_ERROR_BAD_ARGUMENTS;
+  return hal_rpc_pkey_dispatch->get_attributes(pkey, attributes, attributes_len,
+                                               attributes_buffer, attributes_buffer_len);
+}
+
+hal_error_t hal_rpc_pkey_delete_attributes(const hal_pkey_handle_t pkey,
+                                           const uint32_t * const types,
+                                           const unsigned types_len)
+{
+  if (types == NULL || types_len == 0)
+    return HAL_ERROR_BAD_ARGUMENTS;
+  return hal_rpc_pkey_dispatch->delete_attributes(pkey, types, types_len);
+}
+
 /*
  * Local variables:
  * indent-tabs-mode: nil
diff --git a/rpc_client.c b/rpc_client.c
index 0bd3ed5..76c4f0f 100644
--- a/rpc_client.c
+++ b/rpc_client.c
@@ -830,7 +830,7 @@ static hal_error_t pkey_remote_match(const hal_client_handle_t client,
                                      const hal_key_type_t type,
                                      const hal_curve_name_t curve,
                                      const hal_key_flags_t flags,
-                                     hal_rpc_pkey_attribute_t *attributes,
+                                     const hal_rpc_pkey_attribute_t *attributes,
                                      const unsigned attributes_len,
                                      hal_uuid_t *result,
                                      unsigned *result_len,
@@ -869,10 +869,11 @@ static hal_error_t pkey_remote_match(const hal_client_handle_t client,
 
   check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret));
   if (rpc_ret == HAL_OK) {
-    uint32_t array_len, uuid_len;
+    uint32_t array_len;
     *result_len = 0;
     check(hal_xdr_decode_int(&iptr, ilimit, &array_len));
     for (int i = 0; i < array_len; ++i) {
+      uint32_t uuid_len = sizeof(result[i].uuid);
       check(hal_xdr_decode_buffer(&iptr, ilimit, result[i].uuid, &uuid_len));
       if (uuid_len != sizeof(result[i].uuid))
         return HAL_ERROR_KEY_NAME_TOO_LONG;
@@ -958,6 +959,106 @@ static hal_error_t pkey_remote_delete_attribute(const hal_pkey_handle_t pkey,
   return rpc_ret;
 }
 
+static hal_error_t pkey_remote_set_attributes(const hal_pkey_handle_t pkey,
+                                             const hal_rpc_pkey_attribute_t *attributes,
+                                             const unsigned attributes_len)
+{
+  size_t outbuf_len = nargs(4 + 2 * attributes_len);
+  for (int i = 0; i < attributes_len; i++)
+    outbuf_len += pad(attributes[i].length);
+
+  uint8_t outbuf[outbuf_len], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[nargs(3)];
+  const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  hal_client_handle_t dummy_client = {0};
+  hal_error_t rpc_ret;
+
+  check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_SET_ATTRIBUTES));
+  check(hal_xdr_encode_int(&optr, olimit, dummy_client.handle));
+  check(hal_xdr_encode_int(&optr, olimit, pkey.handle));
+  check(hal_xdr_encode_int(&optr, olimit, attributes_len));
+  for (int i = 0; i < attributes_len; i++) {
+    check(hal_xdr_encode_int(&optr, olimit, attributes[i].type));
+    check(hal_xdr_encode_buffer(&optr, olimit, attributes[i].value, attributes[i].length));
+  }
+  check(hal_rpc_send(outbuf, optr - outbuf));
+
+  check(read_matching_packet(RPC_FUNC_PKEY_SET_ATTRIBUTES, inbuf, sizeof(inbuf), &iptr, &ilimit));
+
+  check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret));
+  return rpc_ret;
+}
+
+static hal_error_t pkey_remote_get_attributes(const hal_pkey_handle_t pkey,
+                                              hal_rpc_pkey_attribute_t *attributes,
+                                              const unsigned attributes_len,
+                                              uint8_t *attributes_buffer,
+                                              const size_t attributes_buffer_len)
+{
+  /* inbuf[] here includes one extra word per attribute for padding */
+  uint8_t outbuf[nargs(5 + attributes_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[nargs(3 + 3 * attributes_len) + attributes_buffer_len];
+  const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  hal_client_handle_t dummy_client = {0};
+  hal_error_t rpc_ret;
+
+  check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_GET_ATTRIBUTES));
+  check(hal_xdr_encode_int(&optr, olimit, dummy_client.handle));
+  check(hal_xdr_encode_int(&optr, olimit, pkey.handle));
+  check(hal_xdr_encode_int(&optr, olimit, attributes_len));
+  for (int i = 0; i < attributes_len; i++)
+    check(hal_xdr_encode_int(&optr, olimit, attributes[i].type));
+  check(hal_xdr_encode_int(&optr, olimit, attributes_buffer_len));
+  check(hal_rpc_send(outbuf, optr - outbuf));
+
+  check(read_matching_packet(RPC_FUNC_PKEY_GET_ATTRIBUTES, inbuf, sizeof(inbuf), &iptr, &ilimit));
+
+  check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    uint8_t *abuf = attributes_buffer;
+    uint32_t u32;
+    check(hal_xdr_decode_int(&iptr, ilimit, &u32));
+    if (u32 != attributes_len)
+      return HAL_ERROR_RPC_PROTOCOL_ERROR;
+    for (int i = 0; i < attributes_len; i++) {
+      check(hal_xdr_decode_int(&iptr, ilimit, &u32));
+      if (u32 != attributes[i].type)
+        return HAL_ERROR_RPC_PROTOCOL_ERROR;
+      u32 = attributes_buffer + attributes_buffer_len - abuf;
+      check(hal_xdr_decode_buffer(&iptr, ilimit, abuf, &u32));
+      attributes[i].value  = abuf;
+      attributes[i].length = u32;
+      abuf += u32;
+    }
+  }
+  return rpc_ret;
+}
+
+static hal_error_t pkey_remote_delete_attributes(const hal_pkey_handle_t pkey,
+                                                 const uint32_t *types,
+                                                 const unsigned types_len)
+{
+  uint8_t outbuf[nargs(4 + types_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[nargs(3)];
+  const uint8_t *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  hal_client_handle_t dummy_client = {0};
+  hal_error_t rpc_ret;
+
+  check(hal_xdr_encode_int(&optr, olimit, RPC_FUNC_PKEY_DELETE_ATTRIBUTES));
+  check(hal_xdr_encode_int(&optr, olimit, dummy_client.handle));
+  check(hal_xdr_encode_int(&optr, olimit, pkey.handle));
+  check(hal_xdr_encode_int(&optr, olimit, types_len));
+  for (int i = 0; i < types_len; i++) {
+    check(hal_xdr_encode_int(&optr, olimit, types[i]));
+  }
+  check(hal_rpc_send(outbuf, optr - outbuf));
+
+  check(read_matching_packet(RPC_FUNC_PKEY_DELETE_ATTRIBUTES, inbuf, sizeof(inbuf), &iptr, &ilimit));
+
+  check(hal_xdr_decode_int(&iptr, ilimit, &rpc_ret));
+  return rpc_ret;
+}
+
 #if RPC_CLIENT == RPC_CLIENT_MIXED
 
 /*
@@ -1088,7 +1189,10 @@ const hal_rpc_pkey_dispatch_t hal_rpc_remote_pkey_dispatch = {
   pkey_remote_match,
   pkey_remote_set_attribute,
   pkey_remote_get_attribute,
-  pkey_remote_delete_attribute
+  pkey_remote_delete_attribute,
+  pkey_remote_set_attributes,
+  pkey_remote_get_attributes,
+  pkey_remote_delete_attributes
 };
 
 #if RPC_CLIENT == RPC_CLIENT_MIXED
@@ -1110,7 +1214,10 @@ const hal_rpc_pkey_dispatch_t hal_rpc_mixed_pkey_dispatch = {
   pkey_remote_match,
   pkey_remote_set_attribute,
   pkey_remote_get_attribute,
-  pkey_remote_delete_attribute
+  pkey_remote_delete_attribute,
+  pkey_remote_set_attributes,
+  pkey_remote_get_attributes,
+  pkey_remote_delete_attributes
 };
 #endif /* RPC_CLIENT == RPC_CLIENT_MIXED */
 
diff --git a/rpc_pkey.c b/rpc_pkey.c
index 88b6248..50403d7 100644
--- a/rpc_pkey.c
+++ b/rpc_pkey.c
@@ -954,7 +954,7 @@ static hal_error_t pkey_local_match(const hal_client_handle_t client,
                                     const hal_key_type_t type,
                                     const hal_curve_name_t curve,
                                     const hal_key_flags_t flags,
-                                    hal_rpc_pkey_attribute_t *attributes,
+                                    const hal_rpc_pkey_attribute_t *attributes,
                                     const unsigned attributes_len,
                                     hal_uuid_t *result,
                                     unsigned *result_len,
@@ -1048,6 +1048,78 @@ static hal_error_t pkey_local_delete_attribute(const hal_pkey_handle_t pkey,
   return err;
 }
 
+static hal_error_t pkey_local_set_attributes(const hal_pkey_handle_t pkey,
+                                             const hal_rpc_pkey_attribute_t *attributes,
+                                             const unsigned attributes_len)
+{
+  hal_pkey_slot_t *slot = find_handle(pkey);
+
+  if (slot == NULL)
+    return HAL_ERROR_KEY_NOT_FOUND;
+
+  hal_ks_t *ks = NULL;
+  hal_error_t err;
+
+  if ((err = check_writable(slot->client_handle, slot->flags)) != HAL_OK)
+    return err;
+
+  if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK &&
+      (err = hal_ks_set_attributes(ks, slot, attributes, attributes_len)) == HAL_OK)
+    err = hal_ks_close(ks);
+  else if (ks != NULL)
+    (void) hal_ks_close(ks);
+
+  return err;
+}
+
+static hal_error_t pkey_local_get_attributes(const hal_pkey_handle_t pkey,
+                                             hal_rpc_pkey_attribute_t *attributes,
+                                             const unsigned attributes_len,
+                                             uint8_t *attributes_buffer,
+                                             const size_t attributes_buffer_len)
+{
+  hal_pkey_slot_t *slot = find_handle(pkey);
+
+  if (slot == NULL)
+    return HAL_ERROR_KEY_NOT_FOUND;
+
+  hal_ks_t *ks = NULL;
+  hal_error_t err;
+
+  if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK &&
+      (err = hal_ks_get_attributes(ks, slot, attributes, attributes_len,
+                                   attributes_buffer, attributes_buffer_len)) == HAL_OK)
+    err = hal_ks_close(ks);
+  else if (ks != NULL)
+    (void) hal_ks_close(ks);
+
+  return err;
+}
+
+static hal_error_t pkey_local_delete_attributes(const hal_pkey_handle_t pkey,
+                                                const uint32_t * const types,
+                                                const unsigned types_len)
+{
+  hal_pkey_slot_t *slot = find_handle(pkey);
+
+  if (slot == NULL)
+    return HAL_ERROR_KEY_NOT_FOUND;
+
+  hal_ks_t *ks = NULL;
+  hal_error_t err;
+
+  if ((err = check_writable(slot->client_handle, slot->flags)) != HAL_OK)
+    return err;
+
+  if ((err = ks_open_from_flags(&ks, slot->flags)) == HAL_OK &&
+      (err = hal_ks_delete_attributes(ks, slot, types, types_len)) == HAL_OK)
+    err = hal_ks_close(ks);
+  else if (ks != NULL)
+    (void) hal_ks_close(ks);
+
+  return err;
+}
+
 const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch = {
   pkey_local_load,
   pkey_local_find,
@@ -1066,7 +1138,10 @@ const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch = {
   pkey_local_match,
   pkey_local_set_attribute,
   pkey_local_get_attribute,
-  pkey_local_delete_attribute
+  pkey_local_delete_attribute,
+  pkey_local_set_attributes,
+  pkey_local_get_attributes,
+  pkey_local_delete_attributes
 };
 
 /*
diff --git a/rpc_server.c b/rpc_server.c
index a19a44b..201d028 100644
--- a/rpc_server.c
+++ b/rpc_server.c
@@ -44,6 +44,8 @@
 
 #define pad(n) (((n) + 3) & ~3)
 
+#define nargs(n) ((n) * 4)
+
 static hal_error_t get_version(const uint8_t **iptr, const uint8_t * const ilimit,
                                uint8_t **optr, const uint8_t * const olimit)
 {
@@ -811,6 +813,103 @@ static hal_error_t pkey_delete_attribute(const uint8_t **iptr, const uint8_t * c
     return ret;
 }
 
+static hal_error_t pkey_set_attributes(const uint8_t **iptr, const uint8_t * const ilimit,
+                                       uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_client_handle_t client;
+    hal_pkey_handle_t pkey;
+    uint32_t attributes_len;
+    hal_error_t ret;
+
+    check(hal_xdr_decode_int(iptr, ilimit, &client.handle));
+    check(hal_xdr_decode_int(iptr, ilimit, &pkey.handle));
+    check(hal_xdr_decode_int(iptr, ilimit, &attributes_len));
+
+    hal_rpc_pkey_attribute_t attributes[attributes_len > 0 ? attributes_len : 1];
+
+    for (int i = 0; i < attributes_len; i++) {
+        hal_rpc_pkey_attribute_t *a = &attributes[i];
+        uint32_t value_len;
+        check(hal_xdr_decode_int(iptr, ilimit, &a->type));
+        check(hal_xdr_decode_buffer_in_place(iptr, ilimit, &a->value, &value_len));
+        a->length = value_len;
+    }
+
+    ret = hal_rpc_pkey_set_attributes(pkey, attributes, attributes_len);
+
+    return ret;
+}
+
+static hal_error_t pkey_get_attributes(const uint8_t **iptr, const uint8_t * const ilimit,
+                                       uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_client_handle_t client;
+    hal_pkey_handle_t pkey;
+    uint32_t attributes_len, u32;
+    uint8_t *optr_orig = *optr;
+    hal_error_t ret;
+
+    check(hal_xdr_decode_int(iptr, ilimit, &client.handle));
+    check(hal_xdr_decode_int(iptr, ilimit, &pkey.handle));
+    check(hal_xdr_decode_int(iptr, ilimit, &attributes_len));
+
+    hal_rpc_pkey_attribute_t attributes[attributes_len > 0 ? attributes_len : 1];
+
+    for (int i = 0; i < attributes_len; i++)
+        check(hal_xdr_decode_int(iptr, ilimit, &attributes[i].type));
+
+    check(hal_xdr_decode_int(iptr, ilimit, &u32));
+
+    const size_t attributes_buffer_len = u32;
+
+    if (nargs(1 + 2 * attributes_len) + attributes_buffer_len > olimit - *optr)
+        return HAL_ERROR_RPC_PACKET_OVERFLOW;
+
+    uint8_t attributes_buffer[attributes_buffer_len > 0 ? attributes_buffer_len : 1];
+
+    ret = hal_rpc_pkey_get_attributes(pkey, attributes, attributes_len,
+                                      attributes_buffer, attributes_buffer_len);
+
+    if (ret == HAL_OK) {
+        ret = hal_xdr_encode_int(optr, olimit, attributes_len);
+        for (int i = 0; ret == HAL_OK && i < attributes_len; i++) {
+            ret = hal_xdr_encode_int(optr, olimit, attributes[i].type);
+            if (ret != HAL_OK)
+                break;
+            ret = hal_xdr_encode_buffer(optr, olimit, attributes[i].value, attributes[i].length);
+        }
+    }
+
+    if (ret != HAL_OK)
+        *optr = optr_orig;
+
+    return ret;
+}
+
+static hal_error_t pkey_delete_attributes(const uint8_t **iptr, const uint8_t * const ilimit,
+                                          uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_client_handle_t client;
+    hal_pkey_handle_t pkey;
+    uint32_t types_len;
+    hal_error_t ret;
+
+    check(hal_xdr_decode_int(iptr, ilimit, &client.handle));
+    check(hal_xdr_decode_int(iptr, ilimit, &pkey.handle));
+    check(hal_xdr_decode_int(iptr, ilimit, &types_len));
+
+    uint32_t types[types_len > 0 ? types_len : 1];
+
+    for (int i = 0; i < types_len; i++) {
+        check(hal_xdr_decode_int(iptr, ilimit, &types[i]));
+    }
+
+    ret = hal_rpc_pkey_delete_attributes(pkey, types, types_len);
+
+    return ret;
+}
+
+
 hal_error_t hal_rpc_server_dispatch(const uint8_t * const ibuf, const size_t ilen,
                                     uint8_t * const obuf, size_t * const olen)
 {
@@ -922,6 +1021,15 @@ hal_error_t hal_rpc_server_dispatch(const uint8_t * const ibuf, const size_t ile
     case RPC_FUNC_PKEY_DELETE_ATTRIBUTE:
         handler = pkey_delete_attribute;
         break;
+    case RPC_FUNC_PKEY_SET_ATTRIBUTES:
+        handler = pkey_set_attributes;
+        break;
+    case RPC_FUNC_PKEY_GET_ATTRIBUTES:
+        handler = pkey_get_attributes;
+        break;
+    case RPC_FUNC_PKEY_DELETE_ATTRIBUTES:
+        handler = pkey_delete_attributes;
+        break;
     }
 
     if (handler)
diff --git a/unit-tests.py b/unit-tests.py
index ca18e3a..43b56ba 100644
--- a/unit-tests.py
+++ b/unit-tests.py
@@ -625,20 +625,33 @@ class TestPKeyAttributeWriteSpeedToken(TestCaseLoggedIn):
         self.addCleanup(self.k.delete)
         super(TestPKeyAttributeWriteSpeedToken, self).setUp()
 
-    def set_attributes(self, n_attrs):
+    def set_attributes_single(self, n_attrs):
         pinwheel = Pinwheel()
         for i in xrange(n_attrs):
             pinwheel()
             self.k.set_attribute(i, "Attribute {}".format(i))
 
-    def test_set_1_attribute(self):
-        self.set_attributes(1)
+    def set_attributes_bulk(self, n_attrs):
+        self.k.set_attributes(dict((i, "Attribute {}".format(i))
+                                   for i in xrange(n_attrs)))
 
-    def test_set_6_attributes(self):
-        self.set_attributes(6)
+    def test_set_1_attribute_single(self):
+        self.set_attributes_single(1)
 
-    def test_set_12_attributes(self):
-        self.set_attributes(12)
+    def test_set_6_attributes_single(self):
+        self.set_attributes_single(6)
+
+    def test_set_12_attributes_single(self):
+        self.set_attributes_single(12)
+
+    def test_set_1_attribute_bulk(self):
+        self.set_attributes_bulk(1)
+
+    def test_set_6_attributes_bulk(self):
+        self.set_attributes_bulk(6)
+
+    def test_set_12_attributes_bulk(self):
+        self.set_attributes_bulk(12)
 
 class TestPKeyAttributeWriteSpeedVolatile(TestCaseLoggedIn):
     """
@@ -651,19 +664,33 @@ class TestPKeyAttributeWriteSpeedVolatile(TestCaseLoggedIn):
         self.addCleanup(self.k.delete)
         super(TestPKeyAttributeWriteSpeedVolatile, self).setUp()
 
-    def set_attributes(self, n_attrs):
+    def set_attributes_single(self, n_attrs):
+        pinwheel = Pinwheel()
         for i in xrange(n_attrs):
+            pinwheel()
             self.k.set_attribute(i, "Attribute {}".format(i))
 
-    def test_set_1_attribute(self):
-        self.set_attributes(1)
+    def set_attributes_bulk(self, n_attrs):
+        self.k.set_attributes(dict((i, "Attribute {}".format(i))
+                                   for i in xrange(n_attrs)))
+
+    def test_set_1_attribute_single(self):
+        self.set_attributes_single(1)
+
+    def test_set_6_attributes_single(self):
+        self.set_attributes_single(6)
+
+    def test_set_12_attributes_single(self):
+        self.set_attributes_single(12)
 
-    def test_set_6_attributes(self):
-        self.set_attributes(6)
+    def test_set_1_attribute_bulk(self):
+        self.set_attributes_bulk(1)
 
-    def test_set_12_attributes(self):
-        self.set_attributes(12)
+    def test_set_6_attributes_bulk(self):
+        self.set_attributes_bulk(6)
 
+    def test_set_12_attributes_bulk(self):
+        self.set_attributes_bulk(12)
 
 class TestPKeyAttributeReadSpeedToken(TestCaseLoggedIn):
     """
@@ -674,23 +701,36 @@ class TestPKeyAttributeReadSpeedToken(TestCaseLoggedIn):
         der = PreloadedKey.db[HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256].der
         self.k = hsm.pkey_load(HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256, der, HAL_KEY_FLAG_TOKEN)
         self.addCleanup(self.k.delete)
-        self.k.set_attribute(0, "Attribute 0")
+        for i in xrange(12):
+            self.k.set_attribute(i, "Attribute {}".format(i))
         super(TestPKeyAttributeReadSpeedToken, self).setUp()
 
-    def get_attributes(self, n_attrs):
+    def get_attributes_single(self, n_attrs):
         pinwheel = Pinwheel()
         for i in xrange(n_attrs):
             pinwheel()
-            self.k.get_attribute(0)
+            self.k.get_attribute(i)
+
+    def get_attributes_bulk(self, n_attrs):
+        self.k.get_attributes(range(n_attrs))
+
+    def test_get_1_attribute_single(self):
+        self.get_attributes_single(1)
+
+    def test_get_6_attributes_single(self):
+        self.get_attributes_single(6)
+
+    def test_get_12_attributes_single(self):
+        self.get_attributes_single(12)
 
-    def test_get_1_attribute(self):
-        self.get_attributes(1)
+    def test_get_1_attribute_bulk(self):
+        self.get_attributes_bulk(1)
 
-    def test_get_6_attributes(self):
-        self.get_attributes(6)
+    def test_get_6_attributes_bulk(self):
+        self.get_attributes_bulk(6)
 
-    def test_get_12_attributes(self):
-        self.get_attributes(12)
+    def test_get_12_attributes_bulk(self):
+        self.get_attributes_bulk(12)
 
 class TestPKeyAttributeReadSpeedVolatile(TestCaseLoggedIn):
     """
@@ -701,24 +741,36 @@ class TestPKeyAttributeReadSpeedVolatile(TestCaseLoggedIn):
         der = PreloadedKey.db[HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256].der
         self.k = hsm.pkey_load(HAL_KEY_TYPE_EC_PRIVATE, HAL_CURVE_P256, der, 0)
         self.addCleanup(self.k.delete)
-        self.k.set_attribute(0, "Attribute 0")
+        for i in xrange(12):
+            self.k.set_attribute(i, "Attribute {}".format(i))
         super(TestPKeyAttributeReadSpeedVolatile, self).setUp()
 
-    def get_attributes(self, n_attrs):
+    def get_attributes_single(self, n_attrs):
         pinwheel = Pinwheel()
         for i in xrange(n_attrs):
             pinwheel()
-            self.k.get_attribute(0)
+            self.k.get_attribute(i)
+
+    def get_attributes_bulk(self, n_attrs):
+        self.k.get_attributes(range(n_attrs))
+
+    def test_get_1_attribute_single(self):
+        self.get_attributes_single(1)
+
+    def test_get_6_attributes_single(self):
+        self.get_attributes_single(6)
 
-    def test_get_1_attribute(self):
-        self.get_attributes(1)
+    def test_get_12_attributes_single(self):
+        self.get_attributes_single(12)
 
-    def test_get_6_attributes(self):
-        self.get_attributes(6)
+    def test_get_1_attribute_bulk(self):
+        self.get_attributes_bulk(1)
 
-    def test_get_12_attributes(self):
-        self.get_attributes(12)
+    def test_get_6_attributes_bulk(self):
+        self.get_attributes_bulk(6)
 
+    def test_get_12_attributes_bulk(self):
+        self.get_attributes_bulk(12)
 
 
 @unittest.skipUnless(ecdsa_loaded, "Requires Python ECDSA package")



More information about the Commits mailing list