[Cryptech-Commits] [sw/libhal] 01/03: Merge branch 'logout' into ks9

git at cryptech.is git at cryptech.is
Wed May 31 00:42:00 UTC 2017


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

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

commit f3a7d2993b46e981208ac68996db609a31d80163
Merge: ade02eb 358b380
Author: Rob Austein <sra at hactrn.net>
AuthorDate: Tue May 30 18:54:41 2017 -0400

    Merge branch 'logout' into ks9
    
    The internal keystore API has changed enough since where the "logout"
    branch forked that a plain merge would have no prayer of compiling,
    must less running.  So this merge goes well beyond manual conflict
    resolution: it salvages the useful code from the "logout" branch, with
    additional code as needed to reimplement the functionality.  Sorry.

 hal_internal.h |  9 ++++++
 ks.c           | 12 ++++++++
 ks.h           |  4 ++-
 ks_token.c     |  9 +++++-
 ks_volatile.c  | 31 +++++++++++++++++++-
 rpc_misc.c     | 92 +++++++++++++++++++++++++++++++++++++++++-----------------
 rpc_pkey.c     | 26 +++++++++++++++++
 7 files changed, 153 insertions(+), 30 deletions(-)

diff --cc hal_internal.h
index add7890,13c79e9..2486fd2
--- a/hal_internal.h
+++ b/hal_internal.h
@@@ -477,51 -477,389 +483,54 @@@ typedef struct 
  
  typedef struct hal_ks hal_ks_t;
  
 -struct hal_ks_driver {
 -
 -  hal_error_t (*init)(const hal_ks_driver_t * const driver,
 -                      const int alloc);
 +extern hal_ks_t * const hal_ks_token;
 +extern hal_ks_t * const hal_ks_volatile;
  
 -  hal_error_t (*shutdown)(const hal_ks_driver_t * const driver);
 +extern hal_error_t hal_ks_init(hal_ks_t *ks,
 +                               const int alloc);
  
 -  hal_error_t (*open)(const hal_ks_driver_t * const driver,
 -                      hal_ks_t **ks);
 -
 -  hal_error_t (*close)(hal_ks_t *ks);
 -
 -  hal_error_t (*store)(hal_ks_t *ks,
 -                       hal_pkey_slot_t *slot,
 -                       const uint8_t * const der,  const size_t der_len);
 -
 -  hal_error_t (*fetch)(hal_ks_t *ks,
 -                       hal_pkey_slot_t *slot,
 -                       uint8_t *der, size_t *der_len, const size_t der_max);
 -
 -  hal_error_t (*delete)(hal_ks_t *ks,
 -                        hal_pkey_slot_t *slot);
 -
 -  hal_error_t (*match)(hal_ks_t *ks,
 -                       const hal_client_handle_t client,
 -                       const hal_session_handle_t session,
 -                       const hal_key_type_t type,
 -                       const hal_curve_name_t curve,
 -                       const hal_key_flags_t mask,
 -                       const hal_key_flags_t flags,
 -                       const hal_pkey_attribute_t *attributes,
 -                       const unsigned attributes_len,
 -                       hal_uuid_t *result,
 -                       unsigned *result_len,
 -                       const unsigned result_max,
 -                       const hal_uuid_t * const previous_uuid);
 +extern void hal_ks_init_read_only_pins_only(void);
  
 -  hal_error_t (*set_attributes)(hal_ks_t *ks,
 +extern hal_error_t hal_ks_store(hal_ks_t *ks,
                                  hal_pkey_slot_t *slot,
 -                                const hal_pkey_attribute_t *attributes,
 -                                const unsigned attributes_len);
 +                                const uint8_t * const der, const size_t der_len);
  
 -  hal_error_t (*get_attributes)(hal_ks_t *ks,
 +extern hal_error_t hal_ks_fetch(hal_ks_t *ks,
                                  hal_pkey_slot_t *slot,
 -                                hal_pkey_attribute_t *attributes,
 +                                uint8_t *der, size_t *der_len, const size_t der_max);
 +
 +extern hal_error_t hal_ks_delete(hal_ks_t *ks,
 +                                 hal_pkey_slot_t *slot);
 +
 +extern hal_error_t hal_ks_match(hal_ks_t *ks,
 +                                const hal_client_handle_t client,
 +                                const hal_session_handle_t session,
 +                                const hal_key_type_t type,
 +                                const hal_curve_name_t curve,
 +                                const hal_key_flags_t mask,
 +                                const hal_key_flags_t flags,
 +                                const hal_pkey_attribute_t *attributes,
                                  const unsigned attributes_len,
 -                                uint8_t *attributes_buffer,
 -                                const size_t attributes_buffer_len);
 -
 -  hal_error_t  (*logout)(hal_ks_t *ks,
 -                         const hal_client_handle_t client);
 -
 -};
 -
 -
 -struct hal_ks {
 -  const hal_ks_driver_t *driver;
 -  /*
 -   * Any other common portions of hal_ks_t go here.
 -   */
 -
 -  /*
 -   * Driver-specific stuff is handled by a form of subclassing:
 -   * driver module embeds this structure at the head of whatever
 -   * else it needs, and performs casts as needed.
 -   */
 -};
 -
 -extern const hal_ks_driver_t
 -   hal_ks_volatile_driver[1],
 -   hal_ks_token_driver[1];
 -
 -static inline hal_error_t hal_ks_init(const hal_ks_driver_t * const driver,
 -                                      const int alloc)
 -{
 -  if (driver == NULL)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  if (driver->init == NULL)
 -    return HAL_ERROR_NOT_IMPLEMENTED;
 -
 -  return driver->init(driver, alloc);
 -}
 -
 -static inline hal_error_t hal_ks_shutdown(const hal_ks_driver_t * const driver)
 -{
 -  if (driver == NULL)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  if (driver->shutdown == NULL)
 -    return HAL_ERROR_NOT_IMPLEMENTED;
 -
 -  return driver->shutdown(driver);
 -}
 -
 -static inline hal_error_t hal_ks_open(const hal_ks_driver_t * const driver,
 -                                      hal_ks_t **ks)
 -{
 -  if (driver == NULL || ks == NULL)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  if (driver->open == NULL)
 -    return HAL_ERROR_NOT_IMPLEMENTED;
 -
 -  return driver->open(driver, ks);
 -}
 -
 -static inline hal_error_t hal_ks_close(hal_ks_t *ks)
 -{
 -  if (ks == NULL || ks->driver == NULL)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  if (ks->driver->close == NULL)
 -    return HAL_ERROR_NOT_IMPLEMENTED;
 -
 -  return ks->driver->close(ks);
 -}
 -
 -static inline hal_error_t hal_ks_store(hal_ks_t *ks,
 -                                       hal_pkey_slot_t *slot,
 -                                       const uint8_t * const der,  const size_t der_len)
 -{
 -  if (ks == NULL || ks->driver == NULL || slot == NULL || der == NULL)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  if (ks->driver->store == NULL)
 -    return HAL_ERROR_NOT_IMPLEMENTED;
 -
 -  return ks->driver->store(ks, slot, der, der_len);
 -}
 -
 -static inline hal_error_t hal_ks_fetch(hal_ks_t *ks,
 -                                       hal_pkey_slot_t *slot,
 -                                       uint8_t *der, size_t *der_len, const size_t der_max)
 -{
 -  if (ks == NULL || ks->driver == NULL || slot == NULL)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  if (ks->driver->fetch == NULL)
 -    return HAL_ERROR_NOT_IMPLEMENTED;
 -
 -  return ks->driver->fetch(ks, slot, der, der_len, der_max);
 -}
 -
 -static inline hal_error_t hal_ks_delete(hal_ks_t *ks,
 -                                        hal_pkey_slot_t *slot)
 -{
 -  if (ks == NULL || ks->driver == NULL || slot == NULL)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  if (ks->driver->delete == NULL)
 -    return HAL_ERROR_NOT_IMPLEMENTED;
 -
 -  return ks->driver->delete(ks, slot);
 -}
 -
 -static inline hal_error_t hal_ks_match(hal_ks_t *ks,
 -                                       const hal_client_handle_t client,
 -                                       const hal_session_handle_t session,
 -                                       const hal_key_type_t type,
 -                                       const hal_curve_name_t curve,
 -                                       const hal_key_flags_t mask,
 -                                       const hal_key_flags_t flags,
 -                                       const hal_pkey_attribute_t *attributes,
 -                                       const unsigned attributes_len,
 -                                       hal_uuid_t *result,
 -                                       unsigned *result_len,
 -                                       const unsigned result_max,
 -                                       const hal_uuid_t * const previous_uuid)
 -{
 -  if (ks == NULL || ks->driver == NULL)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  if (ks->driver->match == NULL)
 -    return HAL_ERROR_NOT_IMPLEMENTED;
 -
 -  return ks->driver->match(ks, client, session, type, curve, mask, flags, attributes, attributes_len,
 -                           result, result_len, result_max, previous_uuid);
 -}
 -
 -static inline  hal_error_t hal_ks_set_attributes(hal_ks_t *ks,
 -                                                 hal_pkey_slot_t *slot,
 -                                                 const hal_pkey_attribute_t *attributes,
 -                                                 const unsigned attributes_len)
 -{
 -  if (ks == NULL || ks->driver == NULL || slot == NULL ||
 -      attributes == NULL || attributes_len == 0)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  if (ks->driver->set_attributes == NULL)
 -    return HAL_ERROR_NOT_IMPLEMENTED;
 -
 -  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_pkey_attribute_t *attributes,
 -                                                const unsigned attributes_len,
 -                                                uint8_t *attributes_buffer,
 -                                                const size_t attributes_buffer_len)
 -{
 -  if (ks == NULL || ks->driver == NULL || slot == NULL ||
 -      attributes == NULL || attributes_len == 0)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  if (ks->driver->get_attributes == NULL)
 -    return HAL_ERROR_NOT_IMPLEMENTED;
 -
 -  return ks->driver->get_attributes(ks, slot, attributes, attributes_len,
 -                                    attributes_buffer, attributes_buffer_len);
 -}
 -
 -static inline hal_error_t hal_ks_logout(hal_ks_t *ks,
 -                                        const hal_client_handle_t client)
 -{
 -  if (ks == NULL)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  if (ks->driver->logout == NULL || client.handle == HAL_HANDLE_NONE)
 -    return HAL_OK;
 -
 -  return ks->driver->logout(ks, client);
 -}
 -
 -/*
 - * Keystore index.  This is intended to be usable by both memory-based
 - * (in-memory, mmap(), ...) keystores and keystores based on raw flash.
 - * Some of the features aren't really necessary for memory-based keystores,
 - * but should be harmless.
 - *
 - * General approach is multiple arrays, all but one of which are
 - * indexed by "block" numbers, where a block number might be a slot in
 - * yet another static array, the number of a flash sub-sector, or
 - * whatever is the appropriate unit for holding one keystore record.
 - *
 - * The index array contains nothing but flags and block numbers, and
 - * is deliberately a small data structure so that moving data around
 - * within it is relatively cheap.
 - *
 - * The index array is divided into two portions: the index proper, and
 - * the free queue.  The index proper is ordered according to the names
 - * (UUIDs) of the corresponding blocks; the free queue is a FIFO, to
 - * support a simplistic form of wear leveling in flash-based keystores.
 - *
 - * Key names are kept in a separate array, indexed by block number.
 - * Key names here are a composite of the key's UUID and a "chunk"
 - * number; the latter allows storage of keys whose total size exceeds
 - * one block (whatever a block is).  For the moment we keep the UUID
 - * and the chunk number in a single array, which may provide (very)
 - * slightly better performance due to reference locality in SDRAM, but
 - * this may change if we need to reclaim the space wasted by structure
 - * size rounding.
 - *
 - * The all-zeros UUID, which (by definition) cannot be a valid key
 - * UUID, is reserved for the (non-key) block used to stash PINs and
 - * other small data which aren't really part of the keystore proper
 - * but are kept with it because the keystore is the flash we have.
 - *
 - * Note that this API deliberately says nothing about how the keys
 - * themselves are stored, that's up to the keystore driver.  This
 - * portion of the API is only concerned with allocation and naming.
 - */
 -
 -typedef struct {
 -  hal_uuid_t name;              /* Key name */
 -  uint8_t chunk;                /* Key chunk number */
 -} hal_ks_name_t;
 -
 -typedef struct {
 -  unsigned size;                /* Array length */
 -  unsigned used;                /* How many blocks are in use */
 -  uint16_t *index;              /* Index/freelist array */
 -  hal_ks_name_t *names;         /* Keyname array */
 -} hal_ks_index_t;
 -
 -/*
 - * Finish setting up key index.  Caller must populate index, free
 - * list, and name array.
 - *
 - * This function checks a few things then sorts the index proper.
 - *
 - * If driver cares about wear leveling, driver must supply the free
 - * list in the desired order (FIFO); figuring out what that order is a
 - * problem for the keystore driver.
 - */
 -extern hal_error_t hal_ks_index_setup(hal_ks_index_t *ksi);
 -
 -/*
 - * Find a key block, return its block number.
 - */
 -extern hal_error_t hal_ks_index_find(hal_ks_index_t *ksi,
 -                                     const hal_uuid_t * const name,
 -                                     const unsigned chunk,
 -                                     unsigned *blockno,
 -                                     int *hint);
 -
 -/*
 - * Find all the blocks in a key, return the block numbers.
 - */
 -extern hal_error_t hal_ks_index_find_range(hal_ks_index_t *ksi,
 -                                           const hal_uuid_t * const name,
 -                                           const unsigned max_blocks,
 -                                           unsigned *n_blocks,
 -                                           unsigned *blocknos,
 -                                           int *hint,
 -                                           const int strict);
 -
 -/*
 - * Add a key block, return its block number.
 - */
 -extern hal_error_t hal_ks_index_add(hal_ks_index_t *ksi,
 -                                    const hal_uuid_t * const name,
 -                                    const unsigned chunk,
 -                                    unsigned *blockno,
 -                                    int *hint);
 -
 -/*
 - * Delete a key block, returns its block number (driver may need it).
 - */
 -extern hal_error_t hal_ks_index_delete(hal_ks_index_t *ksi,
 -                                       const hal_uuid_t * const name,
 -                                       const unsigned chunk,
 -                                       unsigned *blockno,
 -                                       int *hint);
 -
 -/*
 - * Delete all of blocks in a key, returning the block numbers.
 - */
 -
 -extern hal_error_t hal_ks_index_delete_range(hal_ks_index_t *ksi,
 -                                             const hal_uuid_t * const name,
 -                                             const unsigned max_blocks,
 -                                             unsigned *n_blocks,
 -                                             unsigned *blocknos,
 -                                             int *hint);
 -
 -/*
 - * Replace a key block with a new one, return new block number.
 - * Name of block does not change.  This is an optimization of
 - * a delete immediately followed by an add for the same name.
 - */
 -
 -extern hal_error_t hal_ks_index_replace(hal_ks_index_t *ksi,
 -                                        const hal_uuid_t * const name,
 -                                        const unsigned chunk,
 -                                        unsigned *blockno,
 -                                        int *hint);
 -
 -/*
 - * Check the index for errors.  At least for the moment, this just
 - * reports errors, it doesn't attempt to fix them.
 - */
 -
 -extern hal_error_t hal_ks_index_fsck(hal_ks_index_t *ksi);
 -
 -/*
 - * Keystore attribute utilities, for use by keystore drivers.
 - */
 -
 -extern const size_t hal_ks_attribute_header_size;
 -
 -extern hal_error_t hal_ks_attribute_scan(const uint8_t * const bytes,
 -                                         const size_t bytes_len,
 +                                hal_uuid_t *result,
 +                                unsigned *result_len,
 +                                const unsigned result_max,
 +                                const hal_uuid_t * const previous_uuid);
 +
 +extern hal_error_t hal_ks_set_attributes(hal_ks_t *ks,
 +                                         hal_pkey_slot_t *slot,
 +                                         const hal_pkey_attribute_t *attributes,
 +                                         const unsigned attributes_len);
 +
 +extern hal_error_t hal_ks_get_attributes(hal_ks_t *ks,
 +                                         hal_pkey_slot_t *slot,
                                           hal_pkey_attribute_t *attributes,
                                           const unsigned attributes_len,
 -                                         size_t *total_len);
 -
 -extern hal_error_t hal_ks_attribute_delete(uint8_t *bytes,
 -                                           const size_t bytes_len,
 -                                           hal_pkey_attribute_t *attributes,
 -                                           unsigned *attributes_len,
 -                                           size_t *total_len,
 -                                           const uint32_t type);
 -
 -extern hal_error_t hal_ks_attribute_insert(uint8_t *bytes, const size_t bytes_len,
 -                                           hal_pkey_attribute_t *attributes,
 -                                           unsigned *attributes_len,
 -                                           size_t *total_len,
 -                                           const uint32_t type,
 -                                           const uint8_t * const value,
 -                                           const size_t value_len);
 +                                         uint8_t *attributes_buffer,
 +                                         const size_t attributes_buffer_len);
 +
++extern hal_error_t hal_ks_logout(hal_ks_t *ks,
++                                 const hal_client_handle_t client);
+ 
  /*
   * RPC lowest-level send and receive routines. These are blocking, and
   * transport-specific (sockets, USB).
diff --cc ks.c
index e966b94,0000000..92dc303
mode 100644,000000..100644
--- a/ks.c
+++ b/ks.c
@@@ -1,897 -1,0 +1,909 @@@
 +/*
 + * ks.c
 + * ----
 + * Keystore, generic parts anyway.  This is internal within libhal.
 + *
 + * Copyright (c) 2015-2017, NORDUnet A/S 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.
 + *
 + * - Neither the name of the NORDUnet nor the names of its contributors may
 + *   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
 + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + */
 +
 +#include <stddef.h>
 +#include <string.h>
 +
 +#include "hal.h"
 +#include "hal_internal.h"
 +#include "ks.h"
 +
 +/*
 + * PIN block gets the all-zeros UUID, which will never be returned by
 + * the UUID generation code (by definition -- it's not a version 4 UUID).
 + */
 +
 +const hal_uuid_t hal_ks_pin_uuid = {{0}};
 +
 +/*
 + * Pick unused or least-recently-used slot in our in-memory cache.
 + *
 + * Updating lru values is caller's problem: if caller is using a cache
 + * slot as a temporary buffer and there's no point in caching the
 + * result, leave the lru values alone and the right thing will happen.
 + */
 +
 +hal_ks_block_t *hal_ks_cache_pick_lru(hal_ks_t *ks)
 +{
 +  uint32_t best_delta = 0;
 +  int      best_index = 0;
 +
 +  for (int i = 0; i < ks->cache_size; i++) {
 +
 +    if (ks->cache[i].blockno == ~0)
 +      return &ks->cache[i].block;
 +
 +    const unsigned delta = ks->cache_lru - ks->cache[i].lru;
 +    if (delta > best_delta) {
 +      best_delta = delta;
 +      best_index = i;
 +    }
 +
 +  }
 +
 +  ks->cache[best_index].blockno = ~0;
 +  return &ks->cache[best_index].block;
 +}
 +
 +/*
 + * Find a block in our in-memory cache; return block or NULL if not present.
 + */
 +
 +hal_ks_block_t *hal_ks_cache_find_block(const hal_ks_t * const ks, const unsigned blockno)
 +{
 +  for (int i = 0; i < ks->cache_size; i++)
 +    if (ks->cache[i].blockno == blockno)
 +      return &ks->cache[i].block;
 +  return NULL;
 +}
 +
 +/*
 + * Mark a block in our in-memory cache as being in current use.
 + */
 +
 +void hal_ks_cache_mark_used(hal_ks_t *ks, const hal_ks_block_t * const block, const unsigned blockno)
 +{
 +  for (int i = 0; i < ks->cache_size; i++) {
 +    if (&ks->cache[i].block == block) {
 +      ks->cache[i].blockno = blockno;
 +      ks->cache[i].lru = ++ks->cache_lru;
 +      return;
 +    }
 +  }
 +}
 +
 +/*
 + * Release a block from the in-memory cache.
 + */
 +
 +void hal_ks_cache_release(hal_ks_t *ks, const hal_ks_block_t * const block)
 +{
 +  if (block != NULL)
 +    hal_ks_cache_mark_used(ks, block, ~0);
 +}
 +
 +/*
 + * Generate CRC-32 for a block.
 + *
 + * This function needs to understand the structure of the
 + * hal_ks_block_header_t, so that it can skip over fields that
 + * shouldn't be included in the CRC.
 + */
 +
 +hal_crc32_t hal_ks_block_calculate_crc(const hal_ks_block_t * const block)
 +{
 +  hal_crc32_t crc = hal_crc32_init();
 +
 +  if (block != NULL) {
 +
 +    crc = hal_crc32_update(crc,  &block->header.block_type,
 +                           sizeof(block->header.block_type));
 +
 +    crc = hal_crc32_update(crc,
 +                           block->bytes   + sizeof(hal_ks_block_header_t),
 +                           sizeof(*block) - sizeof(hal_ks_block_header_t));
 +  }
 +
 +  return hal_crc32_finalize(crc);
 +}
 +
 +/*
 + * Read a block using the cache.  Marking the block as used is left
 + * for the caller, so we can avoid blowing out the cache when we
 + * perform a hal_ks_match() operation.
 + */
 +
 +hal_error_t hal_ks_block_read_cached(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t **block)
 +{
 +  if (block == NULL)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  if ((*block = hal_ks_cache_find_block(ks, blockno)) != NULL)
 +    return HAL_OK;
 +
 +  if ((*block = hal_ks_cache_pick_lru(ks)) == NULL)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  return hal_ks_block_read(ks, blockno, *block);
 +}
 +
 +/*
 + * Update one block, including zombie jamboree.
 + */
 +
 +hal_error_t hal_ks_block_update(hal_ks_t *ks,
 +                                const unsigned b1,
 +                                hal_ks_block_t *block,
 +                                const hal_uuid_t * const uuid,
 +                                int *hint)
 +{
 +  if (block == NULL)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  if (ks->used == ks->size)
 +    return HAL_ERROR_NO_KEY_INDEX_SLOTS;
 +
 +  hal_ks_cache_release(ks, block);
 +
 +  hal_error_t err;
 +  unsigned b2;
 +
 +  if ((err = hal_ks_block_deprecate(ks, b1))            != HAL_OK ||
 +      (err = hal_ks_index_replace(ks, uuid, &b2, hint)) != HAL_OK ||
 +      (err = hal_ks_block_write(ks, b2, block))         != HAL_OK ||
 +      (err = hal_ks_block_copy_owner(ks, b1, b2))       != HAL_OK ||
 +      (err = hal_ks_block_zero(ks, b1))                 != HAL_OK)
 +    return err;
 +
 +  hal_ks_cache_mark_used(ks, block, b2);
 +
 +  /*
 +   * Erase the first block in the free list. In case of restart, this
 +   * puts the block back at the head of the free list.
 +   */
 +
 +  return hal_ks_block_erase_maybe(ks, ks->index[ks->used]);
 +}
 +
 +/*
 + * Initialize keystore.  This includes various tricky bits, some of
 + * which attempt to preserve the free list ordering across reboots, to
 + * improve our simplistic attempt at wear leveling, others attempt to
 + * recover from unclean shutdown.
 + */
 +
 +hal_error_t hal_ks_init(hal_ks_t *ks, const int alloc)
 +{
 +  return
 +    ks == NULL || ks->driver == NULL  ? HAL_ERROR_BAD_ARGUMENTS   :
 +    ks->driver->init == NULL          ? HAL_ERROR_NOT_IMPLEMENTED :
 +    ks->driver->init(ks, alloc);
 +}
 +
 +static inline void *gnaw(uint8_t **mem, size_t *len, const size_t size)
 +{
 +  if (mem == NULL || *mem == NULL || len == NULL || size > *len)
 +    return NULL;
 +  void *ret = *mem;
 +  *mem += size;
 +  *len -= size;
 +  return ret;
 +}
 +
 +hal_error_t hal_ks_alloc_common(hal_ks_t *ks,
 +                                const unsigned ks_blocks,
 +                                const unsigned cache_blocks,
 +                                void **extra,
 +                                const size_t extra_len)
 +{
 +  /*
 +   * We allocate a single big chunk of memory to make it atomic.  We
 +   * need all three of our blocks, so this way either all succeed or
 +   * all fail; we allow our caller to piggyback its own memory needs
 +   * (if any) on ours for the same reason.
 +   */
 +
 +  size_t len = (sizeof(*ks->index) * ks_blocks +
 +                sizeof(*ks->names) * ks_blocks +
 +                sizeof(*ks->cache) * cache_blocks +
 +                extra_len);
 +
 +  uint8_t *mem = hal_allocate_static_memory(len);
 +
 +  if (mem == NULL)
 +    return HAL_ERROR_ALLOCATION_FAILURE;
 +
 +  memset(((uint8_t *) ks) + sizeof(ks->driver), 0,
 +         sizeof(hal_ks_t) - sizeof(ks->driver));
 +  memset(mem, 0, len);
 +
 +  ks->index = gnaw(&mem, &len, sizeof(*ks->index) * ks_blocks);
 +  ks->names = gnaw(&mem, &len, sizeof(*ks->names) * ks_blocks);
 +  ks->cache = gnaw(&mem, &len, sizeof(*ks->cache) * cache_blocks);
 +
 +  ks->size       = ks_blocks;
 +  ks->cache_size = cache_blocks;
 +
 +  if (extra != NULL)
 +    *extra = mem;
 +
 +  return HAL_OK;
 +}
 +
 +hal_error_t hal_ks_init_common(hal_ks_t *ks)
 +{
 +  if (ks->index == NULL || ks->names == NULL || ks->cache == NULL)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  ks->used = 0;
 +
 +  for (int i = 0; i < ks->cache_size; i++)
 +    ks->cache[i].blockno = ~0;
 +
 +  /*
 +   * Scan existing content of keystore to figure out what we've got.
 +   * This gets a bit involved due to the need to recover from things
 +   * like power failures at inconvenient times.
 +   */
 +
 +  hal_ks_block_type_t   block_types[ks->size];
 +  hal_ks_block_status_t block_status[ks->size];
 +  hal_ks_block_t *block = hal_ks_cache_pick_lru(ks);
 +  int first_erased = -1;
 +  hal_error_t err;
 +  uint16_t n = 0;
 +
 +  if (block == NULL)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  for (int i = 0; i < ks->size; i++) {
 +
 +    /*
 +     * Read one block.  If the CRC is bad or the block type is
 +     * unknown, it's old data we don't understand, something we were
 +     * writing when we crashed, or bad flash; in any of these cases,
 +     * we want the block to end up near the end of the free list.
 +     */
 +
 +    err = hal_ks_block_read(ks, i, block);
 +
 +    if (err == HAL_ERROR_KEYSTORE_BAD_CRC || err == HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE)
 +      block_types[i] = HAL_KS_BLOCK_TYPE_UNKNOWN;
 +
 +    else if (err == HAL_OK)
 +      block_types[i] = hal_ks_block_get_type(block);
 +
 +    else
 +      return err;
 +
 +    switch (block_types[i]) {
 +    case HAL_KS_BLOCK_TYPE_KEY:
 +    case HAL_KS_BLOCK_TYPE_PIN:
 +      block_status[i] = hal_ks_block_get_status(block);
 +      break;
 +    default:
 +      block_status[i] = HAL_KS_BLOCK_STATUS_UNKNOWN;
 +    }
 +
 +    /*
 +     * First erased block we see is head of the free list.
 +     */
 +
 +    if (block_types[i] == HAL_KS_BLOCK_TYPE_ERASED && first_erased < 0)
 +      first_erased = i;
 +
 +    /*
 +     * If it's a valid data block, include it in the index.  We remove
 +     * tombstones (if any) below, for now it's easiest to include them
 +     * in the index, so we can look them up by name if we must.
 +     */
 +
 +    const hal_uuid_t *uuid = NULL;
 +
 +    switch (block_types[i]) {
 +    case HAL_KS_BLOCK_TYPE_KEY: uuid = &block->key.name;        break;
 +    case HAL_KS_BLOCK_TYPE_PIN: uuid = &hal_ks_pin_uuid;        break;
 +    default:                    /* Keep GCC happy */            break;
 +    }
 +
 +    if (uuid != NULL) {
 +      ks->names[i] = *uuid;
 +      ks->index[n++] = i;
 +    }
 +  }
 +
 +  ks->used = n;
 +
 +  if (ks->used > ks->size)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  /*
 +   * At this point we've built the (unsorted) index from all the valid
 +   * blocks.  Now we need to insert free and unrecognized blocks into
 +   * the free list in our preferred order.  It's possible that there's
 +   * a better way to do this than linear scan, but this is just
 +   * integer comparisons in a fairly small data set, so it's probably
 +   * not worth trying to optimize.
 +   */
 +
 +  if (n < ks->size)
 +    for (int i = 0; i < ks->size; i++)
 +      if (block_types[i] == HAL_KS_BLOCK_TYPE_ERASED)
 +        ks->index[n++] = i;
 +
 +  if (n < ks->size)
 +    for (int i = first_erased; i < ks->size; i++)
 +      if (block_types[i] == HAL_KS_BLOCK_TYPE_ZEROED)
 +        ks->index[n++] = i;
 +
 +  if (n < ks->size)
 +    for (int i = 0; i < first_erased; i++)
 +      if (block_types[i] == HAL_KS_BLOCK_TYPE_ZEROED)
 +        ks->index[n++] = i;
 +
 +  if (n < ks->size)
 +    for (int i = 0; i < ks->size; i++)
 +      if (block_types[i] == HAL_KS_BLOCK_TYPE_UNKNOWN)
 +        ks->index[n++] = i;
 +
 +  if (ks->used > ks->size)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  /*
 +   * Sort the index, then deal with tombstones.  Tombstones are blocks
 +   * left behind when something bad (like a power failure) happened
 +   * while we updating.  There can be at most one tombstone and one
 +   * live block for a given UUID.  If we find no live block, we need
 +   * to restore it from the tombstone, after which we need to zero the
 +   * tombstone in either case.  The sequence of operations while
 +   * updating is designed so that, barring a bug or a hardware
 +   * failure, we should never lose data.
 +   */
 +
 +  if ((err = hal_ks_index_heapsort(ks)) != HAL_OK)
 +    return err;
 +
 +  for (unsigned b_tomb = 0; b_tomb < ks->size; b_tomb++) {
 +
 +    if (block_status[b_tomb] != HAL_KS_BLOCK_STATUS_TOMBSTONE)
 +      continue;
 +
 +    hal_uuid_t name = ks->names[b_tomb];
 +
 +    int where = -1;
 +
 +    if ((err = hal_ks_index_find(ks, &name, NULL, &where)) != HAL_OK)
 +      return err;
 +
 +    if (b_tomb != ks->index[where]) {
 +      if (ks->used > where + 1 && b_tomb == ks->index[where + 1])
 +        where = where + 1;
 +      else if (0     <= where - 1 && b_tomb == ks->index[where - 1])
 +        where = where - 1;
 +      else
 +        return HAL_ERROR_IMPOSSIBLE;
 +    }
 +
 +    const int matches_next = where + 1 < ks->used && !hal_uuid_cmp(&name, &ks->names[ks->index[where + 1]]);
 +    const int matches_prev = where - 1 >= 0       && !hal_uuid_cmp(&name, &ks->names[ks->index[where - 1]]);
 +
 +    if ((matches_prev && matches_next) ||
 +        (matches_prev && block_status[ks->index[b_tomb - 1]] != HAL_KS_BLOCK_STATUS_LIVE) ||
 +        (matches_next && block_status[ks->index[b_tomb + 1]] != HAL_KS_BLOCK_STATUS_LIVE))
 +      return HAL_ERROR_IMPOSSIBLE;
 +
 +    if (matches_prev || matches_next)  {
 +      memmove(&ks->index[where], &ks->index[where + 1], (ks->size - where - 1) * sizeof(*ks->index));
 +      ks->index[ks->size - 1] = b_tomb;
 +    }
 +
 +    else {
 +      unsigned b_live;
 +      if ((err = hal_ks_block_read(ks, b_tomb, block)) != HAL_OK)
 +        return err;
 +      block->header.block_status = HAL_KS_BLOCK_STATUS_LIVE;
 +      if ((err = hal_ks_index_replace(ks, &name, &b_live, &where)) != HAL_OK ||
 +          (err = hal_ks_block_write(ks, b_live, block))            != HAL_OK)
 +        return err;
 +      block_status[b_live] = HAL_KS_BLOCK_STATUS_LIVE;
 +    }
 +
 +    if ((err = hal_ks_block_zero(ks, b_tomb)) != HAL_OK)
 +      return err;
 +    block_types[ b_tomb] = HAL_KS_BLOCK_TYPE_ZEROED;
 +    block_status[b_tomb] = HAL_KS_BLOCK_STATUS_UNKNOWN;
 +  }
 +
 +  /*
 +   * Erase first block on free list if it's not already erased.
 +   */
 +
 +  if (ks->used < ks->size &&
 +      (err = hal_ks_block_erase_maybe(ks, ks->index[ks->used])) != HAL_OK)
 +    return err;
 +
 +  /*
 +   * And we're finally done.
 +   */
 +
 +  return HAL_OK;
 +}
 +
 +/*
++ * Log a client out of a keystore.
++ */
++
++hal_error_t hal_ks_logout(hal_ks_t *ks, const hal_client_handle_t client)
++{
++  return
++    ks == NULL || ks->driver == NULL ? HAL_ERROR_BAD_ARGUMENTS   :
++    ks->driver->logout == NULL       ? HAL_ERROR_NOT_IMPLEMENTED :
++    ks->driver->logout(ks, client);
++}
++
++/*
 + * Test whether we like a particular key type.
 + */
 +
 +static inline int acceptable_key_type(const hal_key_type_t type)
 +{
 +  switch (type) {
 +  case HAL_KEY_TYPE_RSA_PRIVATE:
 +  case HAL_KEY_TYPE_EC_PRIVATE:
 +  case HAL_KEY_TYPE_RSA_PUBLIC:
 +  case HAL_KEY_TYPE_EC_PUBLIC:
 +    return 1;
 +  default:
 +    return 0;
 +  }
 +}
 +
 +hal_error_t hal_ks_store(hal_ks_t *ks,
 +                         hal_pkey_slot_t *slot,
 +                         const uint8_t * const der, const size_t der_len)
 +{
 +  if (ks == NULL || slot == NULL || der == NULL || der_len == 0 || !acceptable_key_type(slot->type))
 +    return HAL_ERROR_BAD_ARGUMENTS;
 +
 +  hal_error_t err = HAL_OK;
 +  hal_ks_block_t *block;
 +  hal_ks_key_block_t *k;
 +  uint8_t kek[KEK_LENGTH];
 +  size_t kek_len;
 +  unsigned b;
 +
 +  hal_ks_lock();
 +
 +  if ((block = hal_ks_cache_pick_lru(ks)) == NULL) {
 +    err = HAL_ERROR_IMPOSSIBLE;
 +    goto done;
 +  }
 +
 +  k = &block->key;
 +
 +  if ((err = hal_ks_index_add(ks, &slot->name, &b, &slot->hint)) != HAL_OK)
 +    goto done;
 +
 +  hal_ks_cache_mark_used(ks, block, b);
 +
 +  memset(block, 0xFF, sizeof(*block));
 +
 +  block->header.block_type   = HAL_KS_BLOCK_TYPE_KEY;
 +  block->header.block_status = HAL_KS_BLOCK_STATUS_LIVE;
 +
 +  k->name    = slot->name;
 +  k->type    = slot->type;
 +  k->curve   = slot->curve;
 +  k->flags   = slot->flags;
 +  k->der_len = SIZEOF_KS_KEY_BLOCK_DER;
 +  k->attributes_len = 0;
 +
 +  if (ks->used < ks->size)
 +    err = hal_ks_block_erase_maybe(ks, ks->index[ks->used]);
 +
 +  if (err == HAL_OK)
 +    err = hal_mkm_get_kek(kek, &kek_len, sizeof(kek));
 +
 +  if (err == HAL_OK)
 +    err = hal_aes_keywrap(NULL, kek, kek_len, der, der_len, k->der, &k->der_len);
 +
 +  memset(kek, 0, sizeof(kek));
 +
 +  if (err == HAL_OK)
 +    err = hal_ks_block_write(ks, b, block);
 +
 +  if (err == HAL_OK)
 +    err = hal_ks_block_set_owner(ks, b, slot->client, slot->session);
 +
 +  if (err == HAL_OK)
 +    goto done;
 +
 +  memset(block, 0, sizeof(*block));
 +  hal_ks_cache_release(ks, block);
 +  (void) hal_ks_index_delete(ks, &slot->name, NULL, &slot->hint);
 +
 + done:
 +  hal_ks_unlock();
 +  return err;
 +}
 +
 +hal_error_t hal_ks_fetch(hal_ks_t *ks,
 +                         hal_pkey_slot_t *slot,
 +                         uint8_t *der, size_t *der_len, const size_t der_max)
 +{
 +  if (ks == NULL || slot == NULL)
 +    return HAL_ERROR_BAD_ARGUMENTS;
 +
 +  hal_error_t err = HAL_OK;
 +  hal_ks_block_t *block;
 +  unsigned b;
 +
 +  hal_ks_lock();
 +
 +  if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint))         != HAL_OK ||
 +      (err = hal_ks_block_test_owner(ks, b, slot->client, slot->session)) != HAL_OK ||
 +      (err = hal_ks_block_read_cached(ks, b, &block))                     != HAL_OK)
 +    goto done;
 +
 +  if (hal_ks_block_get_type(block) != HAL_KS_BLOCK_TYPE_KEY) {
 +    err = HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE; /* HAL_ERROR_KEY_NOT_FOUND */
 +    goto done;
 +  }
 +
 +  hal_ks_cache_mark_used(ks, block, b);
 +
 +  hal_ks_key_block_t *k = &block->key;
 +
 +  slot->type  = k->type;
 +  slot->curve = k->curve;
 +  slot->flags = k->flags;
 +
 +  if (der == NULL && der_len != NULL)
 +    *der_len = k->der_len;
 +
 +  if (der != NULL) {
 +
 +    uint8_t kek[KEK_LENGTH];
 +    size_t kek_len, der_len_;
 +    hal_error_t err;
 +
 +    if (der_len == NULL)
 +      der_len = &der_len_;
 +
 +    *der_len = der_max;
 +
 +    if ((err = hal_mkm_get_kek(kek, &kek_len, sizeof(kek))) == HAL_OK)
 +      err = hal_aes_keyunwrap(NULL, kek, kek_len, k->der, k->der_len, der, der_len);
 +
 +    memset(kek, 0, sizeof(kek));
 +  }
 +
 + done:
 +  hal_ks_unlock();
 +  return err;
 +}
 +
 +hal_error_t hal_ks_delete(hal_ks_t *ks,
 +                          hal_pkey_slot_t *slot)
 +{
 +  if (ks == NULL || slot == NULL)
 +    return HAL_ERROR_BAD_ARGUMENTS;
 +
 +  hal_error_t err = HAL_OK;
 +  unsigned b;
 +
 +  hal_ks_lock();
 +
 +  if ((err = hal_ks_index_delete(ks, &slot->name, &b, &slot->hint))       != HAL_OK ||
 +      (err = hal_ks_block_test_owner(ks, b, slot->client, slot->session)) != HAL_OK)
 +    goto done;
 +
 +  hal_ks_cache_release(ks, hal_ks_cache_find_block(ks, b));
 +
 +  if ((err = hal_ks_block_zero(ks, b)) != HAL_OK)
 +    goto done;
 +
 +  err = hal_ks_block_erase_maybe(ks, ks->index[ks->used]);
 +
 + done:
 +  hal_ks_unlock();
 +  return err;
 +}
 +
 +static inline hal_error_t locate_attributes(hal_ks_block_t *block,
 +                                            uint8_t **bytes, size_t *bytes_len,
 +                                            unsigned **attrs_len)
 +{
 +  if (block == NULL || bytes == NULL || bytes_len == NULL || attrs_len == NULL)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +
 +  if (hal_ks_block_get_type(block) != HAL_KS_BLOCK_TYPE_KEY)
 +    return HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE;
 +  *attrs_len = &block->key.attributes_len;
 +  *bytes = block->key.der + block->key.der_len;
 +  *bytes_len = SIZEOF_KS_KEY_BLOCK_DER - block->key.der_len;
 +
 +  return HAL_OK;
 +}
 +
 +hal_error_t hal_ks_match(hal_ks_t *ks,
 +                         const hal_client_handle_t client,
 +                         const hal_session_handle_t session,
 +                         const hal_key_type_t type,
 +                         const hal_curve_name_t curve,
 +                         const hal_key_flags_t mask,
 +                         const hal_key_flags_t flags,
 +                         const hal_pkey_attribute_t *attributes,
 +                         const unsigned attributes_len,
 +                         hal_uuid_t *result,
 +                         unsigned *result_len,
 +                         const unsigned result_max,
 +                         const hal_uuid_t * const previous_uuid)
 +{
 +  if (ks == NULL || (attributes == NULL && attributes_len > 0) ||
 +      result == NULL || result_len == NULL || previous_uuid == NULL)
 +    return HAL_ERROR_BAD_ARGUMENTS;
 +
 +  hal_error_t err = HAL_OK;
 +  hal_ks_block_t *block;
 +  int i = -1;
 +
 +  hal_ks_lock();
 +
 +  *result_len = 0;
 +
 +  err = hal_ks_index_find(ks, previous_uuid, NULL, &i);
 +
 +  if (err == HAL_ERROR_KEY_NOT_FOUND)
 +    i--;
 +  else if (err != HAL_OK)
 +    goto done;
 +
 +  while (*result_len < result_max && ++i < ks->used) {
 +
 +    unsigned b = ks->index[i];
 +
 +    if ((err = hal_ks_block_read_cached(ks, b, &block)) != HAL_OK)
 +      goto done;
 +
 +    if ((err = hal_ks_block_test_owner(ks, b, client, session)) == HAL_ERROR_KEY_NOT_FOUND)
 +      continue;
 +
 +    if (err != HAL_OK)
 +      goto done;
 +
 +    if ((type  != HAL_KEY_TYPE_NONE && type  != block->key.type)  ||
 +        (curve != HAL_CURVE_NONE    && curve != block->key.curve) ||
 +        ((flags ^ block->key.flags) & mask)  != 0)
 +      continue;
 +
 +    if (attributes_len > 0) {
 +      uint8_t need_attr[attributes_len];
 +      uint8_t *bytes = NULL;
 +      size_t bytes_len = 0;
 +      unsigned *attrs_len;
 +      int possible = 1;
 +
 +      memset(need_attr, 1, sizeof(need_attr));
 +
 +      if ((err = locate_attributes(block, &bytes, &bytes_len, &attrs_len)) != HAL_OK)
 +        goto done;
 +
 +      if (*attrs_len > 0) {
 +        hal_pkey_attribute_t attrs[*attrs_len];
 +
 +        if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, NULL)) != HAL_OK)
 +          goto done;
 +
 +        for (int j = 0; possible && j < attributes_len; j++) {
 +
 +          if (!need_attr[j])
 +            continue;
 +
 +          for (hal_pkey_attribute_t *a = attrs; a < attrs + *attrs_len; a++) {
 +            if (a->type != attributes[j].type)
 +              continue;
 +            need_attr[j] = 0;
 +            possible = (a->length == attributes[j].length &&
 +                        !memcmp(a->value, attributes[j].value, a->length));
 +            break;
 +          }
 +        }
 +      }
 +
 +      if (!possible || memchr(need_attr, 1, sizeof(need_attr)) != NULL)
 +        continue;
 +    }
 +
 +    result[*result_len] = ks->names[b];
 +    ++*result_len;
 +  }
 +
 +  err = HAL_OK;
 +
 + done:
 +  hal_ks_unlock();
 +  return err;
 +}
 +
 +hal_error_t hal_ks_set_attributes(hal_ks_t *ks,
 +                                  hal_pkey_slot_t *slot,
 +                                  const hal_pkey_attribute_t *attributes,
 +                                  const unsigned attributes_len)
 +{
 +  if (ks == NULL || slot == NULL || attributes == NULL || attributes_len == 0)
 +    return HAL_ERROR_BAD_ARGUMENTS;
 +
 +  hal_error_t err = HAL_OK;
 +  hal_ks_block_t *block;
 +  unsigned b;
 +
 +  hal_ks_lock();
 +
 +  {
 +    if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint))         != HAL_OK ||
 +        (err = hal_ks_block_test_owner(ks, b, slot->client, slot->session)) != HAL_OK ||
 +        (err = hal_ks_block_read_cached(ks, b, &block))                     != HAL_OK)
 +      goto done;
 +
 +    hal_ks_cache_mark_used(ks, block, b);
 +
 +    uint8_t *bytes = NULL;
 +    size_t bytes_len = 0;
 +    unsigned *attrs_len;
 +
 +    if ((err = locate_attributes(block, &bytes, &bytes_len, &attrs_len)) != HAL_OK)
 +      goto done;
 +
 +    hal_pkey_attribute_t attrs[*attrs_len + attributes_len];
 +    size_t total;
 +
 +    if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, &total)) != HAL_OK)
 +      goto done;
 +
 +    for (int i = 0; err == HAL_OK && i < attributes_len; i++)
 +      if (attributes[i].length == HAL_PKEY_ATTRIBUTE_NIL)
 +        err = hal_ks_attribute_delete(bytes, bytes_len, attrs, attrs_len, &total,
 +                                      attributes[i].type);
 +      else
 +        err = hal_ks_attribute_insert(bytes, bytes_len, attrs, attrs_len, &total,
 +                                      attributes[i].type,
 +                                      attributes[i].value,
 +                                      attributes[i].length);
 +
 +    if (err == HAL_OK)
 +      err = hal_ks_block_update(ks, b, block, &slot->name, &slot->hint);
 +    else
 +      hal_ks_cache_release(ks, block);
 +  }
 +
 + done:
 +  hal_ks_unlock();
 +  return err;
 +}
 +
 +hal_error_t hal_ks_get_attributes(hal_ks_t *ks,
 +                                  hal_pkey_slot_t *slot,
 +                                  hal_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)
 +    return HAL_ERROR_BAD_ARGUMENTS;
 +
 +  for (int i = 0; i < attributes_len; i++) {
 +    attributes[i].length = 0;
 +    attributes[i].value  = NULL;
 +  }
 +
 +  uint8_t *abuf = attributes_buffer;
 +  hal_ks_block_t *block = NULL;
 +  hal_error_t err = HAL_OK;
 +  unsigned found = 0;
 +  unsigned b;
 +
 +  hal_ks_lock();
 +
 +  {
 +    if ((err = hal_ks_index_find(ks, &slot->name, &b, &slot->hint))         != HAL_OK ||
 +        (err = hal_ks_block_test_owner(ks, b, slot->client, slot->session)) != HAL_OK ||
 +        (err = hal_ks_block_read_cached(ks, b, &block))                     != HAL_OK)
 +      goto done;
 +
 +    hal_ks_cache_mark_used(ks, block, b);
 +
 +    uint8_t *bytes = NULL;
 +    size_t bytes_len = 0;
 +    unsigned *attrs_len;
 +
 +    if ((err = locate_attributes(block, &bytes, &bytes_len, &attrs_len)) != HAL_OK)
 +      goto done;
 +
 +    if (*attrs_len == 0) {
 +      err = HAL_ERROR_ATTRIBUTE_NOT_FOUND;
 +      goto done;
 +    }
 +
 +    hal_pkey_attribute_t attrs[*attrs_len];
 +
 +    if ((err = hal_ks_attribute_scan(bytes, bytes_len, attrs, *attrs_len, NULL)) != HAL_OK)
 +      goto done;
 +
 +    for (int i = 0; i < attributes_len; i++) {
 +
 +      if (attributes[i].length > 0)
 +        continue;
 +
 +      int j = 0;
 +      while (j < *attrs_len && attrs[j].type != attributes[i].type)
 +        j++;
 +      if (j >= *attrs_len)
 +        continue;
 +      found++;
 +
 +      attributes[i].length = attrs[j].length;
 +
 +      if (attributes_buffer_len == 0)
 +        continue;
 +
 +      if (attrs[j].length > attributes_buffer + attributes_buffer_len - abuf) {
 +        err = HAL_ERROR_RESULT_TOO_LONG;
 +        goto done;
 +      }
 +
 +      memcpy(abuf, attrs[j].value, attrs[j].length);
 +      attributes[i].value  = abuf;
 +      abuf += attrs[j].length;
 +    }
 +
 +  };
 +
 +  if (found < attributes_len && attributes_buffer_len > 0)
 +    err = HAL_ERROR_ATTRIBUTE_NOT_FOUND;
 +  else
 +    err = HAL_OK;
 +
 + done:
 +  hal_ks_unlock();
 +  return err;
 +}
 +
 +/*
 + * Local variables:
 + * indent-tabs-mode: nil
 + * End:
 + */
diff --cc ks.h
index 240d3e6,0000000..1c09b53
mode 100644,000000..100644
--- a/ks.h
+++ b/ks.h
@@@ -1,419 -1,0 +1,421 @@@
 +/*
 + * ks.h
 + * ----
 + * Keystore, generic parts anyway.  This is internal within libhal.
 + *
 + * Copyright (c) 2015-2017, NORDUnet A/S 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.
 + *
 + * - Neither the name of the NORDUnet nor the names of its contributors may
 + *   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
 + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + */
 +
 +#ifndef _KS_H_
 +#define _KS_H_
 +
 +#include "hal.h"
 +#include "hal_internal.h"
 +
 +/*
 + * Size of a keystore "block".
 + *
 + * This must be an integer multiple of the flash subsector size, among
 + * other reasons because that's the minimum erasable unit.
 + */
 +
 +#ifndef HAL_KS_BLOCK_SIZE
 +#define HAL_KS_BLOCK_SIZE       (4096)
 +#endif
 +
 +/*
 + * PIN block gets the all-zeros UUID, which will never be returned by
 + * the UUID generation code (by definition -- it's not a version 4 UUID).
 + */
 +
 +const hal_uuid_t hal_ks_pin_uuid;
 +
 +/*
 + * Known block states.
 + *
 + * C does not guarantee any particular representation for enums, so
 + * including enums directly in the block header isn't safe.  Instead,
 + * we use an access method which casts when reading from the header.
 + * Writing to the header isn't a problem, because C does guarantee
 + * that enum is compatible with *some* integer type, it just doesn't
 + * specify which one.
 + */
 +
 +typedef enum {
 +  HAL_KS_BLOCK_TYPE_ERASED  = 0xFF, /* Pristine erased block (candidate for reuse) */
 +  HAL_KS_BLOCK_TYPE_ZEROED  = 0x00, /* Zeroed block (recently used) */
 +  HAL_KS_BLOCK_TYPE_KEY     = 0x55, /* Block contains key material */
 +  HAL_KS_BLOCK_TYPE_PIN     = 0xAA, /* Block contains PINs */
 +  HAL_KS_BLOCK_TYPE_UNKNOWN = -1,   /* Internal code for "I have no clue what this is" */
 +} hal_ks_block_type_t;
 +
 +/*
 + * Block status.
 + */
 +
 +typedef enum {
 +  HAL_KS_BLOCK_STATUS_LIVE      = 0x66, /* This is a live block */
 +  HAL_KS_BLOCK_STATUS_TOMBSTONE = 0x44, /* This is a tombstone left behind during an update  */
 +  HAL_KS_BLOCK_STATUS_UNKNOWN   = -1,   /* Internal code for "I have no clue what this is" */
 +} hal_ks_block_status_t;
 +
 +/*
 + * Common header for all keystore block types.
 + * A few of these fields are deliberately omitted from the CRC.
 + */
 +
 +typedef struct {
 +  uint8_t               block_type;
 +  uint8_t               block_status;
 +  hal_crc32_t           crc;
 +} hal_ks_block_header_t;
 +
 +/*
 + * Key block.  Tail end of "der" field (after der_len) used for attributes.
 + */
 +
 +typedef struct {
 +  hal_ks_block_header_t header;
 +  hal_uuid_t            name;
 +  hal_key_type_t        type;
 +  hal_curve_name_t      curve;
 +  hal_key_flags_t       flags;
 +  size_t                der_len;
 +  unsigned              attributes_len;
 +  uint8_t               der[];  /* Must be last field -- C99 "flexible array member" */
 +} hal_ks_key_block_t;
 +
 +#define SIZEOF_KS_KEY_BLOCK_DER                                 \
 +  (HAL_KS_BLOCK_SIZE - offsetof(hal_ks_key_block_t, der))
 +
 +/*
 + * PIN block.  Also includes space for backing up the KEK when
 + * HAL_MKM_FLASH_BACKUP_KLUDGE is enabled.
 + */
 +
 +typedef struct {
 +  hal_ks_block_header_t header;
 +  hal_ks_pin_t          wheel_pin;
 +  hal_ks_pin_t          so_pin;
 +  hal_ks_pin_t          user_pin;
 +#if HAL_MKM_FLASH_BACKUP_KLUDGE
 +  uint32_t              kek_set;
 +  uint8_t               kek[KEK_LENGTH];
 +#endif
 +} hal_ks_pin_block_t;
 +
 +#define FLASH_KEK_SET   0x33333333
 +
 +/*
 + * One keystore block.
 + */
 +
 +typedef union {
 +  uint8_t                   bytes[HAL_KS_BLOCK_SIZE];
 +  hal_ks_block_header_t     header;
 +  hal_ks_key_block_t   key;
 +  hal_ks_pin_block_t   pin;
 +} hal_ks_block_t;
 +
 +/*
 + * In-memory cache.
 + */
 +
 +typedef struct {
 +  unsigned              blockno;
 +  unsigned              lru;
 +  hal_ks_block_t        block;
 +} hal_ks_cache_block_t;
 +
 +/*
 + * Keystore object.  hal_internal.h typedefs this to hal_ks_t.
 + *
 + * We expect this to be a static variable, but we expect the arrays in
 + * it to be allocated at runtime using hal_allocate_static_memory()
 + * because they can get kind of large.
 + *
 + * Driver-specific stuff is handled by a form of subclassing: the
 + * driver embeds the hal_ks_t structure at the head of whatever else
 + * it needs, and performs (controlled, type-safe) casts as needed.
 + *
 + * Core of this is the keystore index.  This is intended to be usable
 + * by both memory-based and flash-based keystores.  Some of the
 + * features aren't necessary for memory-based keystores, but should be
 + * harmless, and let us keep the drivers simple.
 + *
 + * General approach is multiple arrays, all but one of which are
 + * indexed by "block" numbers, where a block number might be a slot in
 + * yet another static array, the number of a flash sub-sector, or
 + * whatever is the appropriate unit for holding one keystore record.
 + *
 + * The index array only contains block numbers.  This is a small data
 + * structure so that moving data within it is relatively cheap.
 + *
 + * The index array is divided into two portions: the index proper, and
 + * the free queue.  The index proper is ordered according to the names
 + * (UUIDs) of the corresponding blocks; the free queue is a FIFO, to
 + * support a simplistic form of wear leveling in flash-based keystores.
 + *
 + * Key names are kept in a separate array, indexed by block number.
 + *
 + * The all-zeros UUID, which (by definition) cannot be a valid key
 + * UUID, is reserved for the (non-key) block used to stash PINs and
 + * other small data which aren't really part of the keystore proper
 + * but are kept with it because the keystore is the flash we have.
 + *
 + * Note that this API deliberately says nothing about how the keys
 + * themselves are stored, that's up to the keystore driver.
 + */
 +
 +typedef struct hal_ks_driver hal_ks_driver_t;
 +
 +struct hal_ks {
 +  const hal_ks_driver_t *driver;/* Must be first */
 +  unsigned size;                /* Blocks in keystore */
 +  unsigned used;                /* How many blocks are in use */
 +  uint16_t *index;              /* Index/freelist array */
 +  hal_uuid_t *names;            /* Keyname array */
 +  unsigned cache_lru;           /* Cache LRU counter */
 +  unsigned cache_size;          /* Size (how many blocks) in cache */
 +  hal_ks_cache_block_t *cache;  /* Cache */
 +};
 +
 +/*
 + * Keystore driver.
 + */
 +
 +struct hal_ks_driver {
 +  hal_error_t (*init)        (hal_ks_t *ks, const int alloc);
 +  hal_error_t (*read)        (hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block);
 +  hal_error_t (*write)       (hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block);
 +  hal_error_t (*deprecate)   (hal_ks_t *ks, const unsigned blockno);
 +  hal_error_t (*zero)        (hal_ks_t *ks, const unsigned blockno);
 +  hal_error_t (*erase)       (hal_ks_t *ks, const unsigned blockno);
 +  hal_error_t (*erase_maybe) (hal_ks_t *ks, const unsigned blockno);
 +  hal_error_t (*set_owner)   (hal_ks_t *ks, const unsigned blockno,
 +                              const hal_client_handle_t client, const hal_session_handle_t session);
 +  hal_error_t (*test_owner)  (hal_ks_t *ks, const unsigned blockno,
 +                              const hal_client_handle_t client, const hal_session_handle_t session);
 +  hal_error_t (*copy_owner)  (hal_ks_t *ks, const unsigned source, const unsigned target);
++  hal_error_t (*logout)      (hal_ks_t *ks, const hal_client_handle_t client);
 +};
 +
 +/*
 + * Wrappers around keystore driver methods.
 + *
-  * hal_ks_init() is missing here because we expose it to the rest of libhal.
++ * hal_ks_init() and hal_ks_logout() are missing here because we
++ * expose them to the rest of libhal.
 + */
 +
 +static inline hal_error_t hal_ks_block_read(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block)
 +{
 +  return
 +    ks == NULL || ks->driver == NULL  ? HAL_ERROR_BAD_ARGUMENTS   :
 +    ks->driver->read == NULL          ? HAL_ERROR_NOT_IMPLEMENTED :
 +    ks->driver->read(ks, blockno, block);
 +}
 +
 +static inline hal_error_t hal_ks_block_write(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block)
 +{
 +  return
 +    ks == NULL || ks->driver == NULL  ? HAL_ERROR_BAD_ARGUMENTS   :
 +    ks->driver->write == NULL         ? HAL_ERROR_NOT_IMPLEMENTED :
 +    ks->driver->write(ks, blockno, block);
 +}
 +
 +static inline hal_error_t hal_ks_block_deprecate(hal_ks_t *ks, const unsigned blockno)
 +{
 +  return
 +    ks == NULL || ks->driver == NULL  ? HAL_ERROR_BAD_ARGUMENTS   :
 +    ks->driver->deprecate == NULL     ? HAL_ERROR_NOT_IMPLEMENTED :
 +    ks->driver->deprecate(ks, blockno);
 +}
 +
 +static inline hal_error_t hal_ks_block_zero(hal_ks_t *ks, const unsigned blockno)
 +{
 +  return
 +    ks == NULL || ks->driver == NULL  ? HAL_ERROR_BAD_ARGUMENTS   :
 +    ks->driver->zero == NULL          ? HAL_ERROR_NOT_IMPLEMENTED :
 +    ks->driver->zero(ks, blockno);
 +}
 +
 +static inline hal_error_t hal_ks_block_erase(hal_ks_t *ks, const unsigned blockno)
 +{
 +  return
 +    ks == NULL || ks->driver == NULL  ? HAL_ERROR_BAD_ARGUMENTS   :
 +    ks->driver->erase == NULL         ? HAL_ERROR_NOT_IMPLEMENTED :
 +    ks->driver->erase(ks, blockno);
 +}
 +
 +static inline hal_error_t hal_ks_block_erase_maybe(hal_ks_t *ks, const unsigned blockno)
 +{
 +  return
 +    ks == NULL || ks->driver == NULL  ? HAL_ERROR_BAD_ARGUMENTS   :
 +    ks->driver->erase_maybe == NULL   ? HAL_ERROR_NOT_IMPLEMENTED :
 +    ks->driver->erase_maybe(ks, blockno);
 +}
 +
 +static inline hal_error_t hal_ks_block_set_owner(hal_ks_t *ks,
 +                                                 const unsigned blockno,
 +                                                 const hal_client_handle_t  client,
 +                                                 const hal_session_handle_t session)
 +{
 +  return
 +    ks == NULL || ks->driver == NULL  ? HAL_ERROR_BAD_ARGUMENTS   :
 +    ks->driver->set_owner == NULL     ? HAL_ERROR_NOT_IMPLEMENTED :
 +    ks->driver->set_owner(ks, blockno, client, session);
 +}
 +
 +static inline hal_error_t hal_ks_block_test_owner(hal_ks_t *ks,
 +                                                  const unsigned blockno,
 +                                                  const hal_client_handle_t  client,
 +                                                  const hal_session_handle_t session)
 +{
 +  return
 +    ks == NULL || ks->driver == NULL  ? HAL_ERROR_BAD_ARGUMENTS   :
 +    ks->driver->test_owner == NULL    ? HAL_ERROR_NOT_IMPLEMENTED :
 +    ks->driver->test_owner(ks, blockno, client, session);
 +}
 +
 +static inline hal_error_t hal_ks_block_copy_owner(hal_ks_t *ks,
 +                                                  const unsigned source,
 +                                                  const unsigned target)
 +{
 +  return
 +    ks == NULL || ks->driver == NULL  ? HAL_ERROR_BAD_ARGUMENTS   :
 +    ks->driver->copy_owner == NULL    ? HAL_ERROR_NOT_IMPLEMENTED :
 +    ks->driver->copy_owner(ks, source, target);
 +}
 +
 +/*
 + * Type safe casts.
 + */
 +
 +static inline hal_ks_block_type_t hal_ks_block_get_type(const hal_ks_block_t * const block)
 +{
 +  return block == NULL ? HAL_KS_BLOCK_TYPE_UNKNOWN :
 +    (hal_ks_block_type_t) block->header.block_type;
 +}
 +
 +static inline hal_ks_block_status_t hal_ks_block_get_status(const hal_ks_block_t * const block)
 +{
 +  return block == NULL ? HAL_KS_BLOCK_STATUS_UNKNOWN :
 +    (hal_ks_block_status_t) block->header.block_status;
 +}
 +
 +/*
 + * Keystore utilities.  Some or all of these may end up static within ks.c.
 + */
 +
 +extern hal_error_t hal_ks_alloc_common(hal_ks_t *ks,
 +                                       const unsigned ks_blocks,
 +                                       const unsigned cache_blocks,
 +                                       void **extra,
 +                                       const size_t extra_len);
 +
 +extern hal_error_t hal_ks_init_common(hal_ks_t *ks);
 +
 +extern hal_crc32_t hal_ks_block_calculate_crc(const hal_ks_block_t * const block);
 +
 +extern hal_error_t hal_ks_index_heapsort(hal_ks_t *ks);
 +
 +extern hal_error_t hal_ks_index_find(hal_ks_t *ks,
 +                                     const hal_uuid_t * const name,
 +                                     unsigned *blockno,
 +                                     int *hint);
 +
 +extern hal_error_t hal_ks_index_add(hal_ks_t *ks,
 +                                    const hal_uuid_t * const name,
 +                                    unsigned *blockno,
 +                                    int *hint);
 +
 +extern hal_error_t hal_ks_index_delete(hal_ks_t *ks,
 +                                       const hal_uuid_t * const name,
 +                                       unsigned *blockno,
 +                                       int *hint);
 +
 +extern hal_error_t hal_ks_index_replace(hal_ks_t *ks,
 +                                        const hal_uuid_t * const name,
 +                                        unsigned *blockno,
 +                                        int *hint);
 +
 +extern hal_error_t hal_ks_index_fsck(hal_ks_t *ks);
 +
 +extern const size_t hal_ks_attribute_header_size;
 +
 +extern hal_error_t hal_ks_attribute_scan(const uint8_t * const bytes,
 +                                         const size_t bytes_len,
 +                                         hal_pkey_attribute_t *attributes,
 +                                         const unsigned attributes_len,
 +                                         size_t *total_len);
 +
 +extern hal_error_t hal_ks_attribute_delete(uint8_t *bytes,
 +                                           const size_t bytes_len,
 +                                           hal_pkey_attribute_t *attributes,
 +                                           unsigned *attributes_len,
 +                                           size_t *total_len,
 +                                           const uint32_t type);
 +
 +extern hal_error_t hal_ks_attribute_insert(uint8_t *bytes, const size_t bytes_len,
 +                                           hal_pkey_attribute_t *attributes,
 +                                           unsigned *attributes_len,
 +                                           size_t *total_len,
 +                                           const uint32_t type,
 +                                           const uint8_t * const value,
 +                                           const size_t value_len);
 +
 +extern hal_ks_block_t *hal_ks_cache_pick_lru(hal_ks_t *ks);
 +
 +extern hal_ks_block_t *hal_ks_cache_find_block(const hal_ks_t * const ks,
 +                                               const unsigned blockno);
 +
 +extern void hal_ks_cache_mark_used(hal_ks_t *ks,
 +                                   const hal_ks_block_t * const block,
 +                                   const unsigned blockno);
 +
 +extern void hal_ks_cache_release(hal_ks_t *ks,
 +                                 const hal_ks_block_t * const block);
 +
 +extern hal_error_t hal_ks_block_read_cached(hal_ks_t *ks,
 +                                            const unsigned blockno,
 +                                            hal_ks_block_t **block);
 +
 +extern hal_error_t hal_ks_block_update(hal_ks_t *ks,
 +                                       const unsigned b1,
 +                                       hal_ks_block_t *block,
 +                                       const hal_uuid_t * const uuid,
 +                                       int *hint);
 +
 +#endif /* _KS_H_ */
 +
 +/*
 + * Local variables:
 + * indent-tabs-mode: nil
 + * End:
 + */
diff --cc ks_token.c
index e69eb02,0000000..e29a90d
mode 100644,000000..100644
--- a/ks_token.c
+++ b/ks_token.c
@@@ -1,673 -1,0 +1,680 @@@
 +/*
 + * ks_token.c
 + * ----------
 + * Keystore implementation in flash memory.
 + *
 + * Authors: Rob Austein, Fredrik Thulin
 + * Copyright (c) 2015-2017, NORDUnet A/S 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.
 + *
 + * - Neither the name of the NORDUnet nor the names of its contributors may
 + *   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
 + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + */
 +
 +/*
 + * This keystore driver operates over bare flash, versus over a flash file
 + * system or flash translation layer. The block size is large enough to
 + * hold an AES-keywrapped 4096-bit RSA key. Any remaining space in the key
 + * block may be used to store attributes (opaque TLV blobs). If the
 + * attributes overflow the key block, additional blocks may be added, but
 + * no attribute may exceed the block size.
 + */
 +
 +#include <stddef.h>
 +#include <string.h>
 +#include <assert.h>
 +
 +#include "hal.h"
 +#include "hal_internal.h"
 +#include "ks.h"
 +
 +#include "last_gasp_pin_internal.h"
 +
 +#define HAL_OK CMIS_HAL_OK
 +#include "stm-keystore.h"
 +#undef HAL_OK
 +
 +#ifndef KS_TOKEN_CACHE_SIZE
 +#define KS_TOKEN_CACHE_SIZE 4
 +#endif
 +
 +#define NUM_FLASH_BLOCKS        KEYSTORE_NUM_SUBSECTORS
 +
 +#if HAL_KS_BLOCK_SIZE % KEYSTORE_SUBSECTOR_SIZE != 0
 +#error Keystore block size is not a multiple of flash subsector size
 +#endif
 +
 +/*
 + * Keystore database.
 + */
 +
 +typedef struct {
 +  hal_ks_t              ks;                  /* Must be first (C "subclassing") */
 +  hal_ks_pin_t          wheel_pin;
 +  hal_ks_pin_t          so_pin;
 +  hal_ks_pin_t          user_pin;
 +} ks_token_db_t;
 +
 +/*
 + * This is a bit silly, but it's safe enough, and it lets us avoid a
 + * nasty mess of forward references.
 + */
 +
 +#define db      ((ks_token_db_t * const) hal_ks_token)
 +
 +/*
 + * Calculate offset of the block in the flash address space.
 + */
 +
 +static inline uint32_t ks_token_offset(const unsigned blockno)
 +{
 +  return blockno * KEYSTORE_SUBSECTOR_SIZE;
 +}
 +
 +/*
 + * Read a flash block.
 + *
 + * Flash read on the Alpha is slow enough that it pays to check the
 + * first page before reading the rest of the block.
 + */
 +
 +static hal_error_t ks_token_read(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block)
 +{
 +  if (ks != hal_ks_token || block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  /* Sigh, magic numeric return codes */
 +  if (keystore_read_data(ks_token_offset(blockno),
 +                         block->bytes,
 +                         KEYSTORE_PAGE_SIZE) != 1)
 +    return HAL_ERROR_KEYSTORE_ACCESS;
 +
 +  switch (hal_ks_block_get_type(block)) {
 +  case HAL_KS_BLOCK_TYPE_ERASED:
 +  case HAL_KS_BLOCK_TYPE_ZEROED:
 +    return HAL_OK;
 +  case HAL_KS_BLOCK_TYPE_KEY:
 +  case HAL_KS_BLOCK_TYPE_PIN:
 +    break;
 +  default:
 +    return HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE;
 +  }
 +
 +  switch (hal_ks_block_get_status(block)) {
 +  case HAL_KS_BLOCK_STATUS_LIVE:
 +  case HAL_KS_BLOCK_STATUS_TOMBSTONE:
 +    break;
 +  default:
 +    return HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE;
 +  }
 +
 +  /* Sigh, magic numeric return codes */
 +  if (keystore_read_data(ks_token_offset(blockno) + KEYSTORE_PAGE_SIZE,
 +                         block->bytes + KEYSTORE_PAGE_SIZE,
 +                         sizeof(*block) - KEYSTORE_PAGE_SIZE) != 1)
 +    return HAL_ERROR_KEYSTORE_ACCESS;
 +
 +  if (hal_ks_block_calculate_crc(block) != block->header.crc)
 +    return HAL_ERROR_KEYSTORE_BAD_CRC;
 +
 +  return HAL_OK;
 +}
 +
 +/*
 + * Convert a live block into a tombstone.  Caller is responsible for
 + * making sure that the block being converted is valid; since we don't
 + * need to update the CRC for this, we just modify the first page.
 + */
 +
 +static hal_error_t ks_token_deprecate(hal_ks_t *ks, const unsigned blockno)
 +{
 +  if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  uint8_t page[KEYSTORE_PAGE_SIZE];
 +  hal_ks_block_header_t *header = (void *) page;
 +  uint32_t offset = ks_token_offset(blockno);
 +
 +  /* Sigh, magic numeric return codes */
 +  if (keystore_read_data(offset, page, sizeof(page)) != 1)
 +    return HAL_ERROR_KEYSTORE_ACCESS;
 +
 +  header->block_status = HAL_KS_BLOCK_STATUS_TOMBSTONE;
 +
 +  /* Sigh, magic numeric return codes */
 +  if (keystore_write_data(offset, page, sizeof(page)) != 1)
 +    return HAL_ERROR_KEYSTORE_ACCESS;
 +
 +  return HAL_OK;
 +}
 +
 +/*
 + * Zero (not erase) a flash block.  Just need to zero the first page.
 + */
 +
 +static hal_error_t ks_token_zero(hal_ks_t *ks, const unsigned blockno)
 +{
 +  if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  uint8_t page[KEYSTORE_PAGE_SIZE] = {0};
 +
 +  /* Sigh, magic numeric return codes */
 +  if (keystore_write_data(ks_token_offset(blockno), page, sizeof(page)) != 1)
 +    return HAL_ERROR_KEYSTORE_ACCESS;
 +
 +  return HAL_OK;
 +}
 +
 +/*
 + * Erase a flash block.  Also see ks_token_erase_maybe(), below.
 + */
 +
 +static hal_error_t ks_token_erase(hal_ks_t *ks, const unsigned blockno)
 +{
 +  if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  /* Sigh, magic numeric return codes */
 +  if (keystore_erase_subsector(blockno) != 1)
 +    return HAL_ERROR_KEYSTORE_ACCESS;
 +
 +  return HAL_OK;
 +}
 +
 +/*
 + * Erase a flash block if it hasn't already been erased.
 + * May not be necessary, trying to avoid unnecessary wear.
 + *
 + * Unclear whether there's any sane reason why this needs to be
 + * constant time, given how slow erasure is.  But side channel attacks
 + * can be tricky things, and it's theoretically possible that we could
 + * leak information about, eg, key length, so we do constant time.
 + */
 +
 +static hal_error_t ks_token_erase_maybe(hal_ks_t *ks, const unsigned blockno)
 +{
 +  if (ks != hal_ks_token || blockno >= NUM_FLASH_BLOCKS)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  uint8_t mask = 0xFF;
 +
 +  for (uint32_t a = ks_token_offset(blockno); a < ks_token_offset(blockno + 1); a += KEYSTORE_PAGE_SIZE) {
 +    uint8_t page[KEYSTORE_PAGE_SIZE];
 +    if (keystore_read_data(a, page, sizeof(page)) != 1)
 +      return HAL_ERROR_KEYSTORE_ACCESS;
 +    for (int i = 0; i < KEYSTORE_PAGE_SIZE; i++)
 +      mask &= page[i];
 +  }
 +
 +  return mask == 0xFF ? HAL_OK : ks_token_erase(ks, blockno);
 +}
 +
 +/*
 + * Write a flash block, calculating CRC when appropriate.
 + */
 +
 +static hal_error_t ks_token_write(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block)
 +{
 +  if (ks != hal_ks_token || block == NULL || blockno >= NUM_FLASH_BLOCKS || sizeof(*block) != KEYSTORE_SUBSECTOR_SIZE)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  hal_error_t err = ks_token_erase_maybe(ks, blockno);
 +
 +  if (err != HAL_OK)
 +    return err;
 +
 +  switch (hal_ks_block_get_type(block)) {
 +  case HAL_KS_BLOCK_TYPE_KEY:
 +  case HAL_KS_BLOCK_TYPE_PIN:
 +    block->header.crc = hal_ks_block_calculate_crc(block);
 +    break;
 +  default:
 +    break;
 +  }
 +
 +  /* Sigh, magic numeric return codes */
 +  if (keystore_write_data(ks_token_offset(blockno), block->bytes, sizeof(*block)) != 1)
 +    return HAL_ERROR_KEYSTORE_ACCESS;
 +
 +  return HAL_OK;
 +}
 +
 +/*
 + * The token keystore doesn't implement per-session objects, so these are no-ops.
 + */
 +
 +static hal_error_t ks_token_set_owner(hal_ks_t *ks,
 +                                      const unsigned blockno,
 +                                      const hal_client_handle_t client,
 +                                      const hal_session_handle_t session)
 +{
 +  return HAL_OK;
 +}
 +
 +static hal_error_t ks_token_test_owner(hal_ks_t *ks,
 +                                       const unsigned blockno,
 +                                       const hal_client_handle_t client,
 +                                       const hal_session_handle_t session)
 +{
 +  return HAL_OK;
 +}
 +
 +static hal_error_t ks_token_copy_owner(hal_ks_t *ks,
 +                                       const unsigned source,
 +                                       const unsigned target)
 +{
 +  return HAL_OK;
 +}
 +
++static hal_error_t ks_token_logout(hal_ks_t *ks,
++                                   hal_client_handle_t client)
++{
++  return HAL_OK;
++}
++
 +/*
 + * Forward reference.
 + */
 +
 +static hal_error_t fetch_pin_block(unsigned *b, hal_ks_block_t **block);
 +
 +/*
 + * Initialize keystore.
 + */
 +
 +static hal_error_t ks_token_init(hal_ks_t *ks, const int alloc)
 +{
 +  if (ks != hal_ks_token)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  hal_ks_block_t *block = NULL;
 +  hal_error_t err = HAL_OK;
 +
 +  hal_ks_lock();
 +
 +  if (alloc && (err = hal_ks_alloc_common(ks, NUM_FLASH_BLOCKS, KS_TOKEN_CACHE_SIZE, NULL, 0)) != HAL_OK)
 +    goto done;
 +
 +  if ((err = hal_ks_init_common(ks)) != HAL_OK)
 +    goto done;
 +
 +  /*
 +   * Fetch or create the PIN block.
 +   */
 +
 +  memset(&db->wheel_pin, 0, sizeof(db->wheel_pin));
 +  memset(&db->so_pin,    0, sizeof(db->so_pin));
 +  memset(&db->user_pin,  0, sizeof(db->user_pin));
 +
 +  err = fetch_pin_block(NULL, &block);
 +
 +  if (err == HAL_OK) {
 +    db->wheel_pin = block->pin.wheel_pin;
 +    db->so_pin    = block->pin.so_pin;
 +    db->user_pin  = block->pin.user_pin;
 +  }
 +
 +  else if (err != HAL_ERROR_KEY_NOT_FOUND)
 +    goto done;
 +
 +  else {
 +    /*
 +     * We found no PIN block, so create one, with the user and so PINs
 +     * cleared and the wheel PIN set to the last-gasp value.  The
 +     * last-gasp WHEEL PIN is a terrible answer, but we need some kind
 +     * of bootstrapping mechanism when all else fails.  If you have a
 +     * better suggestion, we'd love to hear it.
 +     */
 +
 +    unsigned b;
 +
 +    if ((block = hal_ks_cache_pick_lru(ks)) == NULL) {
 +      err = HAL_ERROR_IMPOSSIBLE;
 +      goto done;
 +    }
 +
 +    memset(block, 0xFF, sizeof(*block));
 +
 +    block->header.block_type   = HAL_KS_BLOCK_TYPE_PIN;
 +    block->header.block_status = HAL_KS_BLOCK_STATUS_LIVE;
 +
 +    block->pin.wheel_pin = db->wheel_pin = hal_last_gasp_pin;
 +    block->pin.so_pin    = db->so_pin;
 +    block->pin.user_pin  = db->user_pin;
 +
 +    if ((err = hal_ks_index_add(ks, &hal_ks_pin_uuid, &b, NULL)) != HAL_OK)
 +      goto done;
 +
 +    hal_ks_cache_mark_used(ks, block, b);
 +
 +    err = ks_token_write(ks, b, block);
 +
 +    hal_ks_cache_release(ks, block);
 +
 +    if (err != HAL_OK)
 +      goto done;
 +  }
 +
 +  err = HAL_OK;
 +
 + done:
 +  hal_ks_unlock();
 +  return err;
 +}
 +
 +/*
 + * Dispatch vector and keystore definition, now that we've defined all
 + * the driver functions.
 + */
 +
 +static const hal_ks_driver_t ks_token_driver = {
 +  .init                 = ks_token_init,
 +  .read                 = ks_token_read,
 +  .write                = ks_token_write,
 +  .deprecate            = ks_token_deprecate,
 +  .zero                 = ks_token_zero,
 +  .erase                = ks_token_erase,
 +  .erase_maybe          = ks_token_erase_maybe,
 +  .set_owner            = ks_token_set_owner,
 +  .test_owner           = ks_token_test_owner,
-   .copy_owner           = ks_token_copy_owner
++  .copy_owner           = ks_token_copy_owner,
++  .logout               = ks_token_logout
 +};
 +
 +static ks_token_db_t _db = { .ks.driver = &ks_token_driver };
 +
 +hal_ks_t * const hal_ks_token = &_db.ks;
 +
 +/*
 + * The remaining functions aren't really part of the keystore API per se,
 + * but they all involve non-key data which we keep in the keystore
 + * because it's the flash we've got.
 + */
 +
 +/*
 + * Special bonus init routine used only by the bootloader, so that it
 + * can read PINs set by the main firmware.  Yes, this is a kludge.  We
 + * could of course call the real ks_init() routine instead, but it's
 + * slow, and we don't want to allow anything that would modify the
 + * flash here, so having a special entry point for this kludge is
 + * simplest, overall.  Sigh.
 + */
 +
 +void hal_ks_init_read_only_pins_only(void)
 +{
 +  unsigned b, best_seen = ~0;
 +  hal_ks_block_t block[1];
 +
 +  hal_ks_lock();
 +
 +  for (b = 0; b < NUM_FLASH_BLOCKS; b++) {
 +    if (hal_ks_block_read(hal_ks_token, b, block) != HAL_OK ||
 +        hal_ks_block_get_type(block) != HAL_KS_BLOCK_TYPE_PIN)
 +      continue;
 +    best_seen = b;
 +    if (hal_ks_block_get_status(block) == HAL_KS_BLOCK_STATUS_LIVE)
 +      break;
 +  }
 +
 +  if (b != best_seen && best_seen != ~0 &&
 +      hal_ks_block_read(hal_ks_token, best_seen, block) != HAL_OK)
 +    best_seen = ~0;
 +
 +  if (best_seen == ~0) {
 +    memset(block, 0xFF, sizeof(*block));
 +    block->pin.wheel_pin = hal_last_gasp_pin;
 +  }
 +
 +  db->wheel_pin = block->pin.wheel_pin;
 +  db->so_pin    = block->pin.so_pin;
 +  db->user_pin  = block->pin.user_pin;
 +
 +  hal_ks_unlock();
 +}
 +
 +/*
 + * Fetch PIN.  This is always cached, so just returned cached value.
 + */
 +
 +hal_error_t hal_get_pin(const hal_user_t user,
 +                        const hal_ks_pin_t **pin)
 +{
 +  if (pin == NULL)
 +    return HAL_ERROR_BAD_ARGUMENTS;
 +
 +  hal_error_t err = HAL_OK;
 +
 +  hal_ks_lock();
 +
 +  switch (user) {
 +  case HAL_USER_WHEEL:  *pin = &db->wheel_pin;  break;
 +  case HAL_USER_SO:     *pin = &db->so_pin;     break;
 +  case HAL_USER_NORMAL: *pin = &db->user_pin;   break;
 +  default:               err = HAL_ERROR_BAD_ARGUMENTS;
 +  }
 +
 +  hal_ks_unlock();
 +
 +  return err;
 +}
 +
 +/*
 + * Fetch PIN block.  hint = 0 because we know that the all-zeros UUID
 + * should always sort to first slot in the index.
 + */
 +
 +static hal_error_t fetch_pin_block(unsigned *b, hal_ks_block_t **block)
 +{
 +  if (block == NULL)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  hal_error_t err;
 +  int hint = 0;
 +  unsigned b_;
 +
 +  if (b == NULL)
 +    b = &b_;
 +
 +  if ((err = hal_ks_index_find(hal_ks_token, &hal_ks_pin_uuid, b, &hint)) != HAL_OK ||
 +      (err = hal_ks_block_read_cached(hal_ks_token, *b, block))           != HAL_OK)
 +    return err;
 +
 +  hal_ks_cache_mark_used(hal_ks_token, *block, *b);
 +
 +  if (hal_ks_block_get_type(*block) != HAL_KS_BLOCK_TYPE_PIN)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  return HAL_OK;
 +}
 +
 +/*
 + * Update the PIN block.  This block should always be present, but we
 + * have to do the zombie jamboree to make sure we write the new PIN
 + * block before destroying the old one.  hint = 0 because we know that
 + * the all-zeros UUID should always sort to first slot in the index.
 + */
 +
 +static hal_error_t update_pin_block(const unsigned b,
 +                                    hal_ks_block_t *block,
 +                                    const hal_ks_pin_block_t * const new_data)
 +{
 +  if (block == NULL || new_data == NULL || hal_ks_block_get_type(block) != HAL_KS_BLOCK_TYPE_PIN)
 +    return HAL_ERROR_IMPOSSIBLE;
 +
 +  int hint = 0;
 +
 +  block->pin = *new_data;
 +
 +  return hal_ks_block_update(hal_ks_token, b, block, &hal_ks_pin_uuid, &hint);
 +}
 +
 +/*
 + * Change a PIN.
 + */
 +
 +hal_error_t hal_set_pin(const hal_user_t user,
 +                        const hal_ks_pin_t * const pin)
 +{
 +  if (pin == NULL)
 +    return HAL_ERROR_BAD_ARGUMENTS;
 +
 +  hal_ks_block_t *block;
 +  hal_error_t err;
 +  unsigned b;
 +
 +  hal_ks_lock();
 +
 +  if ((err = fetch_pin_block(&b, &block)) != HAL_OK)
 +    goto done;
 +
 +  hal_ks_pin_block_t new_data = block->pin;
 +  hal_ks_pin_t *dp, *bp;
 +
 +  switch (user) {
 +  case HAL_USER_WHEEL:  bp = &new_data.wheel_pin; dp = &db->wheel_pin; break;
 +  case HAL_USER_SO:     bp = &new_data.so_pin;    dp = &db->so_pin;    break;
 +  case HAL_USER_NORMAL: bp = &new_data.user_pin;  dp = &db->user_pin;  break;
 +  default:              err = HAL_ERROR_BAD_ARGUMENTS;  goto done;
 +  }
 +
 +  const hal_ks_pin_t old_pin = *dp;
 +  *dp = *bp = *pin;
 +
 +  if ((err = update_pin_block(b, block, &new_data)) != HAL_OK)
 +    *dp = old_pin;
 +
 + done:
 +  hal_ks_unlock();
 +  return err;
 +}
 +
 +#if HAL_MKM_FLASH_BACKUP_KLUDGE
 +
 +/*
 + * Horrible insecure kludge in lieu of a battery for the MKM.
 + *
 + * API here is a little strange: all calls pass a length parameter,
 + * but any length other than the compiled in constant just returns an
 + * immediate error, there's no notion of buffer max length vs buffer
 + * used length, querying for the size of buffer really needed, or
 + * anything like that.
 + *
 + * We might want to rewrite this some day, if we don't replace it with
 + * a battery first.  For now we just preserve the API as we found it
 + * while re-implementing it on top of the new keystore.
 + */
 +
 +hal_error_t hal_mkm_flash_read_no_lock(uint8_t *buf, const size_t len)
 +{
 +  if (buf != NULL && len != KEK_LENGTH)
 +    return HAL_ERROR_MASTERKEY_BAD_LENGTH;
 +
 +  hal_ks_block_t *block;
 +  hal_error_t err;
 +  unsigned b;
 +
 +  if ((err = fetch_pin_block(&b, &block)) != HAL_OK)
 +    return err;
 +
 +  if (block->pin.kek_set != FLASH_KEK_SET)
 +    return HAL_ERROR_MASTERKEY_NOT_SET;
 +
 +  if (buf != NULL)
 +    memcpy(buf, block->pin.kek, len);
 +
 +  return HAL_OK;
 +}
 +
 +hal_error_t hal_mkm_flash_read(uint8_t *buf, const size_t len)
 +{
 +  hal_ks_lock();
 +  const hal_error_t err = hal_mkm_flash_read_no_lock(buf, len);
 +  hal_ks_unlock();
 +  return err;
 +}
 +
 +hal_error_t hal_mkm_flash_write(const uint8_t * const buf, const size_t len)
 +{
 +  if (buf == NULL)
 +    return HAL_ERROR_BAD_ARGUMENTS;
 +
 +  if (len != KEK_LENGTH)
 +    return HAL_ERROR_MASTERKEY_BAD_LENGTH;
 +
 +  hal_ks_block_t *block;
 +  hal_error_t err;
 +  unsigned b;
 +
 +  hal_ks_lock();
 +
 +  if ((err = fetch_pin_block(&b, &block)) != HAL_OK)
 +    goto done;
 +
 +  hal_ks_pin_block_t new_data = block->pin;
 +
 +  new_data.kek_set = FLASH_KEK_SET;
 +  memcpy(new_data.kek, buf, len);
 +
 +  err = update_pin_block(b, block, &new_data);
 +
 + done:
 +  hal_ks_unlock();
 +  return err;
 +}
 +
 +hal_error_t hal_mkm_flash_erase(const size_t len)
 +{
 +  if (len != KEK_LENGTH)
 +    return HAL_ERROR_MASTERKEY_BAD_LENGTH;
 +
 +  hal_ks_block_t *block;
 +  hal_error_t err;
 +  unsigned b;
 +
 +  hal_ks_lock();
 +
 +  if ((err = fetch_pin_block(&b, &block)) != HAL_OK)
 +    goto done;
 +
 +  hal_ks_pin_block_t new_data = block->pin;
 +
 +  new_data.kek_set = FLASH_KEK_SET;
 +  memset(new_data.kek, 0, len);
 +
 +  err = update_pin_block(b, block, &new_data);
 +
 + done:
 +  hal_ks_unlock();
 +  return err;
 +}
 +
 +#endif /* HAL_MKM_FLASH_BACKUP_KLUDGE */
 +
 +/*
 + * Local variables:
 + * indent-tabs-mode: nil
 + * End:
 + */
diff --cc ks_volatile.c
index 02054ff,6a17e45..57806e8
--- a/ks_volatile.c
+++ b/ks_volatile.c
@@@ -117,117 -228,328 +117,145 @@@ static hal_error_t ks_volatile_zero(hal
    return HAL_OK;
  }
  
 -static inline int acceptable_key_type(const hal_key_type_t type)
 -{
 -  switch (type) {
 -  case HAL_KEY_TYPE_RSA_PRIVATE:
 -  case HAL_KEY_TYPE_EC_PRIVATE:
 -  case HAL_KEY_TYPE_RSA_PUBLIC:
 -  case HAL_KEY_TYPE_EC_PUBLIC:
 -    return 1;
 -  default:
 -    return 0;
 -  }
 -}
 +/*
 + * Erase a flash block.
 + */
  
 -static hal_error_t ks_store(hal_ks_t *ks,
 -                            hal_pkey_slot_t *slot,
 -                            const uint8_t * const der, const size_t der_len)
 +static hal_error_t ks_volatile_erase(hal_ks_t *ks, const unsigned blockno)
  {
 -  if (ks == NULL || slot == NULL || der == NULL || der_len == 0 || !acceptable_key_type(slot->type))
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  ks_t *ksv = ks_to_ksv(ks);
 -  hal_error_t err = HAL_OK;
 -  unsigned b;
 -
 -  hal_ks_lock();
 -
 -  if (ksv->db == NULL) {
 -    err = HAL_ERROR_KEYSTORE_ACCESS;
 -    goto done;
 -  }
 -
 -  if ((err = hal_ks_index_add(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
 -    goto done;
 -
 -  uint8_t kek[KEK_LENGTH];
 -  size_t kek_len;
 -  ks_key_t k;
 -
 -  memset(&k, 0, sizeof(k));
 -  k.der_len = sizeof(k.der);
 -  k.type    = slot->type;
 -  k.curve   = slot->curve;
 -  k.flags   = slot->flags;
 -  k.client  = slot->client_handle;
 -  k.session = slot->session_handle;
 -
 -  if ((err = hal_mkm_get_kek(kek, &kek_len, sizeof(kek))) == HAL_OK)
 -    err = hal_aes_keywrap(NULL, kek, kek_len, der, der_len, k.der, &k.der_len);
 -
 -  memset(kek, 0, sizeof(kek));
 +  if (ks != hal_ks_volatile || db->keys == NULL || blockno >= ks->size)
 +    return HAL_ERROR_IMPOSSIBLE;
  
 -  if (err == HAL_OK)
 -    ksv->db->keys[b] = k;
 -  else
 -    (void) hal_ks_index_delete(&ksv->db->ksi, &slot->name, 0, NULL, &slot->hint);
 +  memset(&db->keys[blockno].block, 0xFF, sizeof(db->keys[blockno].block));
 +  db->keys[blockno].client.handle = HAL_HANDLE_NONE;
 +  db->keys[blockno].session.handle = HAL_HANDLE_NONE;
  
 - done:
 -  hal_ks_unlock();
 -  return err;
 +  return HAL_OK;
  }
  
 -static hal_error_t ks_fetch(hal_ks_t *ks,
 -                            hal_pkey_slot_t *slot,
 -                            uint8_t *der, size_t *der_len, const size_t der_max)
 -{
 -  if (ks == NULL || slot == NULL)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  ks_t *ksv = ks_to_ksv(ks);
 -  hal_error_t err = HAL_OK;
 -  unsigned b;
 -
 -  hal_ks_lock();
 -
 -  if (ksv->db == NULL) {
 -    err = HAL_ERROR_KEYSTORE_ACCESS;
 -    goto done;
 -  }
 -
 -  if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
 -    goto done;
 -
 -  const ks_key_t * const k = &ksv->db->keys[b];
 -
 -  if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k)) {
 -    err = HAL_ERROR_KEY_NOT_FOUND;
 -    goto done;
 -  }
 -
 -  slot->type  = k->type;
 -  slot->curve = k->curve;
 -  slot->flags = k->flags;
 -
 -  if (der == NULL && der_len != NULL)
 -    *der_len = k->der_len;
 -
 -  if (der != NULL) {
 -
 -    uint8_t kek[KEK_LENGTH];
 -    size_t kek_len, der_len_;
 -
 -    if (der_len == NULL)
 -      der_len = &der_len_;
 -
 -    *der_len = der_max;
 -
 -    if ((err = hal_mkm_get_kek(kek, &kek_len, sizeof(kek))) == HAL_OK)
 -      err = hal_aes_keyunwrap(NULL, kek, kek_len, k->der, k->der_len, der, der_len);
 -
 -    memset(kek, 0, sizeof(kek));
 -  }
 -
 - done:
 -  hal_ks_unlock();
 -  return err;
 -}
 +/*
 + * Write a flash block.  CRC probably not necessary for RAM.
 + */
  
 -static hal_error_t ks_delete(hal_ks_t *ks,
 -                             hal_pkey_slot_t *slot)
 +static hal_error_t ks_volatile_write(hal_ks_t *ks, const unsigned blockno, hal_ks_block_t *block)
  {
 -  if (ks == NULL || slot == NULL)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  ks_t *ksv = ks_to_ksv(ks);
 -  hal_error_t err = HAL_OK;
 -  unsigned b;
 -
 -  hal_ks_lock();
 -
 -  if (ksv->db == NULL) {
 -    err = HAL_ERROR_KEYSTORE_ACCESS;
 -    goto done;
 -  }
 -
 -  if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
 -    goto done;
 -
 -  if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, &ksv->db->keys[b])) {
 -    err = HAL_ERROR_KEY_NOT_FOUND;
 -    goto done;
 -  }
 -
 -  if ((err = hal_ks_index_delete(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
 -    goto done;
 +  if (ks != hal_ks_volatile || db->keys == NULL || block == NULL || blockno >= ks->size)
 +    return HAL_ERROR_IMPOSSIBLE;
  
 -  memset(&ksv->db->keys[b], 0, sizeof(ksv->db->keys[b]));
 +  memcpy(&db->keys[blockno].block, block, sizeof(*block));
  
 - done:
 -  hal_ks_unlock();
 -  return err;
 +  return HAL_OK;
  }
  
 -static hal_error_t ks_match(hal_ks_t *ks,
 -                            hal_client_handle_t client,
 -                            hal_session_handle_t session,
 -                            const hal_key_type_t type,
 -                            const hal_curve_name_t curve,
 -                            const hal_key_flags_t mask,
 -                            const hal_key_flags_t flags,
 -                            const hal_pkey_attribute_t *attributes,
 -                            const unsigned attributes_len,
 -                            hal_uuid_t *result,
 -                            unsigned *result_len,
 -                            const unsigned result_max,
 -                            const hal_uuid_t * const previous_uuid)
 +/*
 + * Set key ownership.
 + */
 +
 +static hal_error_t ks_volatile_set_owner(hal_ks_t *ks,
 +                                         const unsigned blockno,
 +                                         const hal_client_handle_t client,
 +                                         const hal_session_handle_t session)
  {
 -  if (ks == NULL || (attributes == NULL && attributes_len > 0) ||
 -      result == NULL || result_len == NULL || previous_uuid == NULL)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 +  if (ks != hal_ks_volatile || db->keys == NULL || blockno >= ks->size)
 +    return HAL_ERROR_IMPOSSIBLE;
  
 -  ks_t *ksv = ks_to_ksv(ks);
 +  db->keys[blockno].client = client;
 +  db->keys[blockno].session = session;
  
 -  if (ksv->db == NULL)
 -    return HAL_ERROR_KEYSTORE_ACCESS;
 +  return HAL_OK;
 +}
  
 -  hal_error_t err = HAL_OK;
 -  int i = -1;
 +/*
 + * Test key ownership.
 + *
 + * One might expect this to be based on whether the session matches,
 + * and indeed it would be in a sane world, but in the world of PKCS
 + * #11, keys belong to sessions, are visible to other sessions, and
 + * may even be modifiable by other sessions, but softly and silently
 + * vanish away when the original creating session is destroyed.
 + *
 + * In our terms, this means that visibility of session objects is
 + * determined only by the client handle, so taking the session handle
 + * as an argument here isn't really necessary, but we've flipflopped
 + * on that enough times that at least for now I'd prefer to leave the
 + * session handle here and not have to revise all the RPC calls again.
 + * Remove it at some later date and redo the RPC calls if we manage to
 + * avoid revising this yet again.
 + */
  
 -  hal_ks_lock();
 +static hal_error_t ks_volatile_test_owner(hal_ks_t *ks,
 +                                          const unsigned blockno,
 +                                          const hal_client_handle_t client,
 +                                          const hal_session_handle_t session)
 +{
 +  if (ks != hal_ks_volatile || db->keys == NULL || blockno >= ks->size)
 +    return HAL_ERROR_IMPOSSIBLE;
  
 -  *result_len = 0;
 +  if (db->keys[blockno].client.handle == HAL_HANDLE_NONE ||
 +      db->keys[blockno].client.handle == client.handle)
 +    return HAL_OK;
  
 -  err = hal_ks_index_find(&ksv->db->ksi, previous_uuid, 0, NULL, &i);
 +  if (hal_rpc_is_logged_in(client, HAL_USER_WHEEL) == HAL_OK)
 +    return HAL_OK;
  
 -  if (err == HAL_ERROR_KEY_NOT_FOUND)
 -    i--;
 -  else if (err != HAL_OK)
 -    goto done;
 +  return HAL_ERROR_KEY_NOT_FOUND;
 +}
  
 -  while (*result_len < result_max && ++i < ksv->db->ksi.used) {
 +/*
 + * Copy key ownership.
 + */
  
 -    unsigned b = ksv->db->ksi.index[i];
 +static hal_error_t ks_volatile_copy_owner(hal_ks_t *ks,
 +                                          const unsigned source,
 +                                          const unsigned target)
 +{
 +  if (ks != hal_ks_volatile || db->keys == NULL || source >= ks->size || target >= ks->size)
 +    return HAL_ERROR_IMPOSSIBLE;
  
 -    if (ksv->db->ksi.names[b].chunk > 0)
 -      continue;
 +  db->keys[target].client  = db->keys[source].client;
 +  db->keys[target].session = db->keys[source].session;
 +  return HAL_OK;
 +}
  
 -    if (type != HAL_KEY_TYPE_NONE && type != ksv->db->keys[b].type)
 -      continue;
 +/*
++ * Zero any blocks owned by a client that we're logging out.
++ */
+ 
 -    if (curve != HAL_CURVE_NONE && curve != ksv->db->keys[b].curve)
 -      continue;
++static hal_error_t ks_volatile_logout(hal_ks_t *ks,
++                                      hal_client_handle_t client)
++{
++  if (ks != hal_ks_volatile || client.handle == HAL_HANDLE_NONE)
++    return HAL_ERROR_IMPOSSIBLE;
+ 
 -    if (((flags ^ ksv->db->keys[b].flags) & mask) != 0)
 -      continue;
++  for (int i = 0; i < ks->used; i++) {
++    unsigned b = ks->index[i];
++    hal_error_t err;
++    int hint = i;
+ 
 -    if (!key_visible_to_session(ksv, client, session, &ksv->db->keys[b]))
++    if (db->keys[b].client.handle != client.handle)
+       continue;
+ 
 -    if (attributes_len > 0) {
 -      const ks_key_t * const k = &ksv->db->keys[b];
 -      int ok = 1;
 -
 -      if (k->attributes_len == 0)
 -        continue;
 -
 -      hal_pkey_attribute_t key_attrs[k->attributes_len];
 -
 -      if ((err = hal_ks_attribute_scan(k->der + k->der_len, sizeof(k->der) - k->der_len,
 -                                       key_attrs, k->attributes_len, NULL)) != HAL_OK)
 -        goto done;
 -
 -      for (const hal_pkey_attribute_t *required = attributes;
 -           ok && required < attributes + attributes_len; required++) {
 -
 -        hal_pkey_attribute_t *present = key_attrs;
 -        while (ok && present->type != required->type)
 -          ok = ++present < key_attrs + k->attributes_len;
 -
 -        if (ok)
 -          ok = (present->length == required->length &&
 -                !memcmp(present->value, required->value, present->length));
 -      }
++    if ((err = hal_ks_index_delete(ks, &ks->names[b], 0, NULL, &hint)) != HAL_OK ||
++        (err = hal_ks_block_zero(ks, b))                               != HAL_OK)
++      return err;
+ 
 -      if (!ok)
 -        continue;
 -    }
 -
 -    result[*result_len] = ksv->db->ksi.names[b].name;
 -    ++*result_len;
++    i--;
+   }
+ 
 -  err = HAL_OK;
 -
 - done:
 -  hal_ks_unlock();
 -  return err;
++  return HAL_OK;
+ }
+ 
 -static hal_error_t ks_set_attributes(hal_ks_t *ks,
 -                                     hal_pkey_slot_t *slot,
 -                                     const hal_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 = HAL_OK;
 -  unsigned b;
 -
 -  hal_ks_lock();
 -
 -  {
 -    if (ksv->db == NULL) {
 -      err = HAL_ERROR_KEYSTORE_ACCESS;
 -      goto done;
 -    }
 -
 -    if ((err = hal_ks_index_find(&ksv->db->ksi, &slot->name, 0, &b, &slot->hint)) != HAL_OK)
 -      goto done;
 -
 -    ks_key_t * const k = &ksv->db->keys[b];
 -
 -    if (!key_visible_to_session(ksv, slot->client_handle, slot->session_handle, k)) {
 -      err = HAL_ERROR_KEY_NOT_FOUND;
 -      goto done;
 -    }
 -
 -    hal_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)
 -      goto done;
 -
 -    for (const hal_pkey_attribute_t *a = attributes; a < attributes + attributes_len; a++) {
 -      if (a->length == HAL_PKEY_ATTRIBUTE_NIL)
 -        err =  hal_ks_attribute_delete(bytes, bytes_len, attrs, &k->attributes_len, &total_len,
 -                                       a->type);
 -      else
 -        err =  hal_ks_attribute_insert(bytes, bytes_len, attrs, &k->attributes_len, &total_len,
 -                                       a->type, a->value, a->length);
 -      if (err != HAL_OK)
 -        goto done;
 -    }
 -
 -    err = HAL_OK;
 -
 -  }
 -
 - done:
 -  hal_ks_unlock();
 -  return err;
 -}
++/*
 + * Initialize keystore.
 + */
  
 -static hal_error_t ks_get_attributes(hal_ks_t *ks,
 -                                     hal_pkey_slot_t *slot,
 -                                     hal_pkey_attribute_t *attributes,
 -                                     const unsigned attributes_len,
 -                                     uint8_t *attributes_buffer,
 -                                     const size_t attributes_buffer_len)
 +static hal_error_t ks_volatile_init(hal_ks_t *ks, const int alloc)
  {
 -  if (ks == NULL || slot == NULL || attributes == NULL || attributes_len == 0 ||
 -      attributes_buffer == NULL)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 +  if (ks != hal_ks_volatile)
 +    return HAL_ERROR_IMPOSSIBLE;
  
 -  ks_t *ksv = ks_to_ksv(ks);
    hal_error_t err = HAL_OK;
 -  unsigned b;
 +  void *mem = NULL;
  
    hal_ks_lock();
  
@@@ -257,27 -614,48 +285,28 @@@
    return err;
  }
  
 -static hal_error_t ks_logout(hal_ks_t *ks,
 -                             hal_client_handle_t client)
 -{
 -  if (ks == NULL || client.handle == HAL_HANDLE_NONE)
 -    return HAL_ERROR_BAD_ARGUMENTS;
 -
 -  ks_t *ksv = ks_to_ksv(ks);
 -  hal_error_t err = HAL_OK;
 -
 -  hal_ks_lock();
 -
 -  for (int i = 0; i < ksv->db->ksi.used; i++) {
 -    unsigned b = ksv->db->ksi.index[i];
 -    if (ksv->db->keys[b].client.handle == client.handle) {
 -      int hint = i;
 -      if ((err = hal_ks_index_delete(&ksv->db->ksi, &ksv->db->ksi.names[b].name, 0, NULL, &hint)) != HAL_OK)
 -        goto done;
 -      memset(&ksv->db->keys[b], 0, sizeof(ksv->db->keys[b]));
 -      i--;
 -    }
 -  }
 -
 - done:
 -  hal_ks_unlock();
 -  return err;
 -}
 +/*
 + * Dispatch vector and keystore definition, now that we've defined all
 + * the driver functions.
 + */
  
 -const hal_ks_driver_t hal_ks_volatile_driver[1] = {{
 +static const hal_ks_driver_t ks_volatile_driver = {
    .init                 = ks_volatile_init,
 -  .shutdown             = ks_volatile_shutdown,
 -  .open                 = ks_volatile_open,
 -  .close                = ks_volatile_close,
 -  .store                = ks_store,
 -  .fetch                = ks_fetch,
 -  .delete               = ks_delete,
 -  .match                = ks_match,
 -  .set_attributes       = ks_set_attributes,
 -  .get_attributes       = ks_get_attributes,
 -  .logout		= ks_logout
 -}};
 -
 -#endif /* STATIC_KS_VOLATILE_SLOTS > 0 */
 +  .read                 = ks_volatile_read,
 +  .write                = ks_volatile_write,
 +  .deprecate            = ks_volatile_deprecate,
 +  .zero                 = ks_volatile_zero,
 +  .erase                = ks_volatile_erase,
 +  .erase_maybe          = ks_volatile_erase, /* sic */
 +  .set_owner            = ks_volatile_set_owner,
 +  .test_owner           = ks_volatile_test_owner,
-   .copy_owner           = ks_volatile_copy_owner
++  .copy_owner           = ks_volatile_copy_owner,
++  .logout               = ks_volatile_logout
 +};
 +
 +static ks_volatile_db_t _db = { .ks.driver = &ks_volatile_driver };
 +
 +hal_ks_t * const hal_ks_volatile = &_db.ks;
  
  /*
   * Local variables:
diff --cc rpc_misc.c
index 3f466bb,e9ff4c6..6e64af2
--- a/rpc_misc.c
+++ b/rpc_misc.c
@@@ -33,8 -33,8 +33,6 @@@
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
--#include <assert.h>
--
  #include "hal.h"
  #include "hal_internal.h"
  
@@@ -46,7 -46,7 +44,8 @@@ static hal_error_t get_version(uint32_
  
  static hal_error_t get_random(void *buffer, const size_t length)
  {
--  assert(buffer != NULL && length > 0);
++  if (buffer == NULL || length == 0)
++    return HAL_ERROR_IMPOSSIBLE;
  
    return hal_get_random(NULL, buffer, length);
  }
@@@ -87,16 -87,8 +86,16 @@@ typedef struct 
  
  static uint32_t hal_pin_default_iterations = HAL_PIN_DEFAULT_ITERATIONS;
  
 +/*
 + * Seconds to delay when given a bad PIN.
 + */
 +
 +#ifndef HAL_PIN_DELAY_ON_FAILURE
 +#define HAL_PIN_DELAY_ON_FAILURE 5
 +#endif
 +
  #ifndef HAL_STATIC_CLIENT_STATE_BLOCKS
--#define HAL_STATIC_CLIENT_STATE_BLOCKS	10
++#define HAL_STATIC_CLIENT_STATE_BLOCKS  10
  #endif
  
  #if HAL_STATIC_CLIENT_STATE_BLOCKS > 0
@@@ -109,7 -101,8 +108,8 @@@ static client_slot_t client_handle[HAL_
   * them.  HAL_USER_NONE indicates an empty slot in the table.
   */
  
- static inline client_slot_t *alloc_slot(void)
 -static inline client_slot_t *alloc_slot(const hal_client_handle_t client,
 -                                        const hal_user_t user)
++static inline hal_error_t alloc_slot(const hal_client_handle_t client,
++                                     const hal_user_t user)
  {
    client_slot_t *slot = NULL;
    hal_critical_section_start();
@@@ -118,10 -117,35 +124,35 @@@
    for (int i = 0; slot == NULL && i < sizeof(client_handle)/sizeof(*client_handle); i++)
      if (client_handle[i].logged_in == HAL_USER_NONE)
        slot = &client_handle[i];
+ 
  #endif
  
+   if (slot != NULL) {
+     slot->handle = client;
+     slot->logged_in = user;
+   }
+ 
    hal_critical_section_end();
--  return slot;
++  return slot == NULL ? HAL_ERROR_NO_CLIENT_SLOTS_AVAILABLE : HAL_OK;
+ }
+ 
+ static inline hal_error_t clear_slot(client_slot_t *slot)
+ {
+   if (slot == NULL)
+     return HAL_OK;
+ 
+   hal_error_t err;
+ 
+   if ((err = hal_pkey_logout(slot->handle)) != HAL_OK)
+     return err;
+ 
+   hal_critical_section_start();
+ 
+   memset(slot, 0, sizeof(*slot));
+ 
+   hal_critical_section_end();
+ 
+   return HAL_OK;
  }
  
  static inline client_slot_t *find_handle(const hal_client_handle_t handle)
@@@ -143,8 -167,8 +174,8 @@@ static hal_error_t login(const hal_clie
                           const hal_user_t user,
                           const char * const pin, const size_t pin_len)
  {
--  assert(pin != NULL && pin_len != 0);
--  assert(user == HAL_USER_NORMAL || user == HAL_USER_SO || user == HAL_USER_WHEEL);
++  if (pin == NULL || pin_len == 0 || (user != HAL_USER_NORMAL && user != HAL_USER_SO && user != HAL_USER_WHEEL))
++    return HAL_ERROR_IMPOSSIBLE;
  
    const hal_ks_pin_t *p;
    hal_error_t err;
@@@ -163,26 -187,19 +194,19 @@@
    for (int i = 0; i < sizeof(buf); i++)
      diff |= buf[i] ^ p->pin[i];
  
 -  if (diff != 0)
 +  if (diff != 0) {
 +    hal_sleep(HAL_PIN_DELAY_ON_FAILURE);
      return HAL_ERROR_PIN_INCORRECT;
 +  }
  
-   client_slot_t *slot = find_handle(client);
- 
-   if (slot == NULL && (slot = alloc_slot()) == NULL)
 -  if (alloc_slot(client, user) == NULL)
--    return HAL_ERROR_NO_CLIENT_SLOTS_AVAILABLE;
- 
-   slot->handle = client;
-   slot->logged_in = user;
--
--  return HAL_OK;
++  return alloc_slot(client, user);
  }
  
  static hal_error_t is_logged_in(const hal_client_handle_t client,
                                  const hal_user_t user)
  {
--  assert(user == HAL_USER_NORMAL || user == HAL_USER_SO || user == HAL_USER_WHEEL);
++  if (user != HAL_USER_NORMAL && user != HAL_USER_SO && user != HAL_USER_WHEEL)
++    return HAL_ERROR_IMPOSSIBLE;
  
    client_slot_t *slot = find_handle(client);
  
@@@ -204,11 -216,34 +223,29 @@@ static hal_error_t logout(const hal_cli
  
  static hal_error_t logout_all(void)
  {
 -  /*
 -   * This is a bit inefficient, but it lets us keep the control
 -   * structure simple.
 -   */
 +#if HAL_STATIC_CLIENT_STATE_BLOCKS > 0
-   for (int i = 0; i < sizeof(client_handle)/sizeof(*client_handle); i++)
-     client_handle[i].logged_in = HAL_USER_NONE;
+ 
+   client_slot_t *slot;
+   hal_error_t err;
++  int i = 0;
+ 
+   do {
 -    slot = NULL;
 -
 -#if HAL_STATIC_CLIENT_STATE_BLOCKS > 0
+ 
+     hal_critical_section_start();
+ 
 -    for (int i = 0; slot == NULL && i < sizeof(client_handle)/sizeof(*client_handle); i++)
++    for (slot = NULL; slot == NULL && i < sizeof(client_handle)/sizeof(*client_handle); i++)
+       if (client_handle[i].logged_in != HAL_USER_NONE)
+         slot = &client_handle[i];
+ 
+     hal_critical_section_end();
+ 
 -#endif
 -
+     if ((err = clear_slot(slot)) != HAL_OK)
+       return err;
+ 
+   } while (slot != NULL);
+ 
 +#endif
 +
    return HAL_OK;
  }
  
@@@ -216,7 -251,7 +253,8 @@@ static hal_error_t set_pin(const hal_cl
                             const hal_user_t user,
                             const char * const newpin, const size_t newpin_len)
  {
--  assert(newpin != NULL && newpin_len >= hal_rpc_min_pin_length && newpin_len <= hal_rpc_max_pin_length);
++  if (newpin == NULL || newpin_len < hal_rpc_min_pin_length || newpin_len > hal_rpc_max_pin_length)
++    return HAL_ERROR_IMPOSSIBLE;
  
    if ((user != HAL_USER_NORMAL || is_logged_in(client, HAL_USER_SO) != HAL_OK) &&
        is_logged_in(client, HAL_USER_WHEEL) != HAL_OK)
diff --cc rpc_pkey.c
index ff61580,5af6c0e..3d4a379
--- a/rpc_pkey.c
+++ b/rpc_pkey.c
@@@ -129,6 -129,47 +129,32 @@@ static inline hal_pkey_slot_t *find_han
  }
  
  /*
+  * Clean up key state associated with a client when logging out.
+  */
+ 
+ hal_error_t hal_pkey_logout(const hal_client_handle_t client)
+ {
+   if (client.handle == HAL_HANDLE_NONE)
+     return HAL_OK;
+ 
+   hal_error_t err;
 -  hal_ks_t *ks;
+ 
 -  if ((err = hal_ks_open(hal_ks_volatile_driver, &ks)) != HAL_OK)
 -    return err;
 -  if ((err = hal_ks_logout(ks, client)) == HAL_OK)
 -    err = hal_ks_close(ks);
 -  else
 -    (void) hal_ks_close(ks);
 -  if (err != HAL_OK)
 -    return err;
 -
 -  if ((err = hal_ks_open(hal_ks_token_driver, &ks)) != HAL_OK)
 -    return err;
 -  if ((err = hal_ks_logout(ks, client)) == HAL_OK)
 -    err = hal_ks_close(ks);
 -  else
 -    (void) hal_ks_close(ks);
 -  if (err != HAL_OK)
++  if ((err = hal_ks_logout(hal_ks_volatile, client)) != HAL_OK ||
++      (err = hal_ks_logout(hal_ks_token,    client)) != HAL_OK)
+     return err;
+ 
+   hal_critical_section_start();
+ 
+   for (int i = 0; i < sizeof(pkey_slot)/sizeof(*pkey_slot); i++)
 -    if (pkey_slot[i].pkey_handle.handle == client.handle)
++    if (pkey_slot[i].pkey.handle == client.handle)
+       memset(&pkey_slot[i], 0, sizeof(pkey_slot[i]));
+ 
+   hal_critical_section_end();
+ 
+   return HAL_OK;
+ }
+ 
+ /*
   * Access rules are a bit complicated, mostly due to PKCS #11.
   *
   * The simple, obvious rule would be that one must be logged in as



More information about the Commits mailing list