[Cryptech-Commits] [sw/libhal] branch rpc updated: RPC over loopback socket, just to work out the mechanics for serialization and dispatch.

git at cryptech.is git at cryptech.is
Fri Feb 26 04:11:52 UTC 2016


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

paul at psgd.org pushed a commit to branch rpc
in repository sw/libhal.

The following commit(s) were added to refs/heads/rpc by this push:
       new  cef7ba6   RPC over loopback socket, just to work out the mechanics for serialization and dispatch.
cef7ba6 is described below

commit cef7ba6f7024a2c3a53760be8c5fc4f937e8efb5
Author: Paul Selkirk <paul at psgd.org>
AuthorDate: Thu Feb 25 17:27:33 2016 -0500

    RPC over loopback socket, just to work out the mechanics for serialization and dispatch.
---
 GNUmakefile             |  19 +-
 hal.h                   |   1 +
 hal_internal.h          |  95 ++++++-
 ks.c                    |   8 +
 rpc_api.c               |  78 ++----
 rpc_client.c            | 525 +++++++++++++++++++++++++++++++++--
 rpc_client_loopback.c   |  85 ++++++
 rpc_hash.c              |  22 +-
 rpc_server.c            | 709 ++++++++++++++++++++++++++++++++++++++++++++++++
 rpc_server_loopback.c   |  89 ++++++
 rpc_xdr.c               | 245 +++++++++++++++++
 tests/GNUmakefile       |   1 +
 tests/test-rpc_hash.c   | 698 +++++++++++++++++++++++++++++++++++++++++++++++
 tests/test-rpc_server.c |  16 ++
 14 files changed, 2489 insertions(+), 102 deletions(-)

diff --git a/GNUmakefile b/GNUmakefile
index a600dd3..0637f76 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -46,18 +46,13 @@ IO_OBJ_I2C 	= hal_io_i2c.o
 # Default I/O bus is EIM, override this to use I2C instead
 IO_OBJ		= ${IO_OBJ_EIM}
 
-RPC_OBJ_COMMON	= rpc_api.o rpc_hash.o
-RPC_OBJ_CLIENT	= ${RPC_OBJ_COMMON} rpc_client.o
-RPC_OBJ_SERVER	= ${RPC_OBJ_COMMON} rpc_misc.o rpc_pkey.o
-
-# Default should be to build the RPC server code, but we haven't
-# written even the skeleton of that yet.  We'll probably end up
-# needing a makefile conditional to handle all this properly
-RPC_OBJ		= ${RPC_OBJ_SERVER}
-
-# XXX temporary
-$(warning TEMPORARY KLUDGE TO TEST rpc_client)
-RPC_OBJ		+= ${RPC_OBJ_CLIENT}
+RPC_OBJ_COMMON	= rpc_api.o rpc_hash.o rpc_misc.o rpc_pkey.o rpc_xdr.o
+RPC_OBJ_CLIENT	= rpc_client.o rpc_client_loopback.o
+RPC_OBJ_SERVER	= rpc_server.o rpc_server_loopback.o
+
+# Default should be to build the RPC server code. We'll probably end up
+# needing a makefile conditional to handle all this properly.
+RPC_OBJ		= ${RPC_OBJ_COMMON} ${RPC_OBJ_CLIENT} ${RPC_OBJ_SERVER}
 
 KS_OBJ_COMMON	= ks.o
 KS_OBJ_MMAP	= ${KS_OBJ_COMMON} ks_mmap.o
diff --git a/hal.h b/hal.h
index bf5ca6f..f3b59ad 100644
--- a/hal.h
+++ b/hal.h
@@ -125,6 +125,7 @@
   DEFINE_HAL_ERROR(HAL_ERROR_PIN_INCORRECT,             "PIN incorrect")                                \
   DEFINE_HAL_ERROR(HAL_ERROR_NO_CLIENT_SLOTS_AVAILABLE, "No client slots available")                    \
   DEFINE_HAL_ERROR(HAL_ERROR_FORBIDDEN,                 "Forbidden")                                    \
+  DEFINE_HAL_ERROR(HAL_ERROR_RPC_TRANSPORT,             "RPC transport error")                          \
   END_OF_HAL_ERROR_LIST
 
 /* Marker to forestall silly line continuation errors */
diff --git a/hal_internal.h b/hal_internal.h
index ea760cf..692067e 100644
--- a/hal_internal.h
+++ b/hal_internal.h
@@ -194,9 +194,9 @@ typedef struct {
 } hal_rpc_pkey_dispatch_t;
 
 
-extern const hal_rpc_misc_dispatch_t hal_rpc_local_misc_dispatch, hal_rpc_remote_misc_dispatch;
-extern const hal_rpc_hash_dispatch_t hal_rpc_local_hash_dispatch, hal_rpc_remote_hash_dispatch;
-extern const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch, hal_rpc_remote_pkey_dispatch, hal_rpc_mixed_pkey_dispatch;
+extern const hal_rpc_misc_dispatch_t hal_rpc_local_misc_dispatch, hal_rpc_remote_misc_dispatch, *hal_rpc_misc_dispatch;
+extern const hal_rpc_hash_dispatch_t hal_rpc_local_hash_dispatch, hal_rpc_remote_hash_dispatch, *hal_rpc_hash_dispatch;
+extern const hal_rpc_pkey_dispatch_t hal_rpc_local_pkey_dispatch, hal_rpc_remote_pkey_dispatch, hal_rpc_mixed_pkey_dispatch, *hal_rpc_pkey_dispatch;
 
 /*
  * Keystore API.
@@ -326,6 +326,95 @@ extern hal_error_t hal_ks_get_pin(const hal_user_t user,
 extern hal_error_t hal_ks_set_pin(const hal_user_t user,
                                   const hal_ks_pin_t * const pin);
 
+/*
+ * RPC serialization/deserialization routines, using XDR (RFC 4506) encoding.
+ */
+
+hal_error_t rpc_encode_int(uint8_t ** const outbuf,
+                           const uint8_t * const limit,
+                           const uint32_t value);
+
+hal_error_t rpc_decode_int(uint8_t ** const inbuf,
+                           const uint8_t * const limit,
+                           uint32_t * const value);
+
+hal_error_t rpc_encode_buffer(uint8_t ** const outbuf,
+                              const uint8_t * const limit,
+                              const uint8_t * const value,
+                              const uint32_t len);
+
+hal_error_t rpc_decode_buffer_in_place(uint8_t ** const inbuf,
+                                       const uint8_t * const limit,
+                                       uint8_t ** const vptr,
+                                       uint32_t * const len);
+
+hal_error_t rpc_decode_buffer(uint8_t ** const inbuf,
+                              const uint8_t * const limit,
+                              uint8_t * const value,
+                              uint32_t * const len);
+
+/* XXX move to hal.h? */
+typedef enum {
+    RPC_LOCAL,
+    RPC_REMOTE,
+    RPC_MIXED,
+} rpc_locality_t;
+
+/*
+ * RPC lowest-level send and receive routines. These are blocking, and
+ * transport-specific (sockets, USB).
+ */
+
+hal_error_t rpc_send(const uint8_t * const buf, const size_t len);
+hal_error_t rpc_recv(uint8_t * const buf, size_t * const len);
+
+hal_error_t rpc_client_init(rpc_locality_t locality);
+hal_error_t rpc_client_close(void);
+hal_error_t rpc_client_transport_init(void);
+hal_error_t rpc_client_transport_close(void);
+
+hal_error_t rpc_sendto(const uint8_t * const buf, const size_t len, void *opaque);
+hal_error_t rpc_recvfrom(uint8_t * const buf, size_t * const len, void **opaque);
+
+hal_error_t rpc_server_init(void);
+hal_error_t rpc_server_close(void);
+hal_error_t rpc_server_transport_init(void);
+hal_error_t rpc_server_transport_close(void);
+void rpc_server_main(void);
+
+/*
+ * RPC function numbers
+ */
+
+typedef enum {
+    RPC_FUNC_GET_RANDOM,
+    RPC_FUNC_SET_PIN,
+    RPC_FUNC_LOGIN,
+    RPC_FUNC_LOGOUT,
+    RPC_FUNC_LOGOUT_ALL,
+    RPC_FUNC_IS_LOGGED_IN,
+    RPC_FUNC_HASH_GET_DIGEST_LEN,
+    RPC_FUNC_HASH_GET_DIGEST_ALGORITHM_ID,
+    RPC_FUNC_HASH_GET_ALGORITHM,
+    RPC_FUNC_HASH_INITIALIZE,
+    RPC_FUNC_HASH_UPDATE,
+    RPC_FUNC_HASH_FINALIZE,
+    RPC_FUNC_PKEY_LOAD,
+    RPC_FUNC_PKEY_FIND,
+    RPC_FUNC_PKEY_GENERATE_RSA,
+    RPC_FUNC_PKEY_GENERATE_EC,
+    RPC_FUNC_PKEY_CLOSE,
+    RPC_FUNC_PKEY_DELETE,
+    RPC_FUNC_PKEY_GET_KEY_TYPE,
+    RPC_FUNC_PKEY_GET_KEY_FLAGS,
+    RPC_FUNC_PKEY_GET_PUBLIC_KEY_LEN,
+    RPC_FUNC_PKEY_GET_PUBLIC_KEY,
+    RPC_FUNC_PKEY_REMOTE_SIGN,
+    RPC_FUNC_PKEY_REMOTE_VERIFY,
+    RPC_FUNC_PKEY_LIST,
+} rpc_func_num_t;
+
+
 #endif /* _HAL_INTERNAL_H_ */
 
 /*
diff --git a/ks.c b/ks.c
index a856bbf..24cafca 100644
--- a/ks.c
+++ b/ks.c
@@ -294,6 +294,14 @@ hal_error_t hal_ks_get_pin(const hal_user_t user,
   return HAL_OK;
 }
 
+hal_error_t hal_ks_get_kek(uint8_t *kek,
+                           size_t *kek_len,
+                           const size_t kek_max)
+{
+# warning Stub out hal_ks_get_kek() for now
+    return HAL_ERROR_IMPOSSIBLE;
+}
+
 /*
  * Local variables:
  * indent-tabs-mode: nil
diff --git a/rpc_api.c b/rpc_api.c
index d0ed25c..b494ca8 100644
--- a/rpc_api.c
+++ b/rpc_api.c
@@ -4,7 +4,7 @@
  * Remote procedure call public API implementation.
  *
  * Authors: Rob Austein
- * Copyright (c) 2015, NORDUnet A/S All rights reserved.
+ * Copyright (c) 2015-2016, 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
@@ -36,32 +36,6 @@
 #include "hal.h"
 #include "hal_internal.h"
 
-#ifndef HAL_RPC_IS_CLIENT
-#warning HAL_RPC_IS_CLIENT not set, assuming we're building for the HSM
-#define HAL_RPC_IS_CLIENT 0
-#endif
-
-/*
- * Maybe we'll let the client configure this at runtime, later.  For
- * now, wire in the obvious defaults: hashing is done locally,
- * everything else is done via RPC.  For the server everything is
- * always done locally.
- */
-
-#if HAL_RPC_IS_CLIENT
-
-static const hal_rpc_misc_dispatch_t * const misc_dispatch = &hal_rpc_remote_misc_dispatch;
-static const hal_rpc_hash_dispatch_t * const hash_dispatch = &hal_rpc_remote_hash_dispatch;
-static const hal_rpc_pkey_dispatch_t * const pkey_dispatch = &hal_rpc_mixed_pkey_dispatch;
-
-#else
-
-static const hal_rpc_misc_dispatch_t * const misc_dispatch = &hal_rpc_local_misc_dispatch;
-static const hal_rpc_hash_dispatch_t * const hash_dispatch = &hal_rpc_local_hash_dispatch;
-static const hal_rpc_pkey_dispatch_t * const pkey_dispatch = &hal_rpc_local_pkey_dispatch;
-
-#endif
-
 const hal_hash_handle_t hal_hash_handle_none = {0};
 
 static inline int check_pkey_type(const hal_key_type_t type)
@@ -120,7 +94,7 @@ hal_error_t hal_rpc_get_random(void *buffer, const size_t length)
     return HAL_ERROR_BAD_ARGUMENTS;
   if (length == 0)
     return HAL_OK;
-  return misc_dispatch->get_random(buffer, length);
+  return hal_rpc_misc_dispatch->get_random(buffer, length);
 }
 
 #warning Perhaps we should be enforcing a minimum PIN length here
@@ -131,7 +105,7 @@ hal_error_t hal_rpc_set_pin(const hal_client_handle_t client,
 {
   if (newpin == NULL || newpin_len == 0 || (user != HAL_USER_NORMAL && user != HAL_USER_SO && user != HAL_USER_WHEEL))
     return HAL_ERROR_BAD_ARGUMENTS;
-  return misc_dispatch->set_pin(client, user, newpin, newpin_len);
+  return hal_rpc_misc_dispatch->set_pin(client, user, newpin, newpin_len);
 }
 
 hal_error_t hal_rpc_login(const hal_client_handle_t client,
@@ -140,17 +114,17 @@ hal_error_t hal_rpc_login(const hal_client_handle_t client,
 {
   if (pin == NULL || pin_len == 0 || (user != HAL_USER_NORMAL && user != HAL_USER_SO && user != HAL_USER_WHEEL))
     return HAL_ERROR_BAD_ARGUMENTS;
-  return misc_dispatch->login(client, user, pin, pin_len);
+  return hal_rpc_misc_dispatch->login(client, user, pin, pin_len);
 }
 
 hal_error_t hal_rpc_logout(const hal_client_handle_t client)
 {
-  return misc_dispatch->logout(client);
+  return hal_rpc_misc_dispatch->logout(client);
 }
 
 hal_error_t hal_rpc_logout_all(void)
 {
-  return misc_dispatch->logout_all();
+  return hal_rpc_misc_dispatch->logout_all();
 }
 
 hal_error_t hal_rpc_is_logged_in(const hal_client_handle_t client,
@@ -158,27 +132,27 @@ hal_error_t hal_rpc_is_logged_in(const hal_client_handle_t client,
 {
   if (user != HAL_USER_NORMAL && user != HAL_USER_SO && user != HAL_USER_WHEEL)
     return HAL_ERROR_BAD_ARGUMENTS;
-  return misc_dispatch->is_logged_in(client, user);
+  return hal_rpc_misc_dispatch->is_logged_in(client, user);
 }
 
 hal_error_t hal_rpc_hash_get_digest_length(const hal_digest_algorithm_t alg, size_t *length)
 {
   if (length == NULL)
     return HAL_ERROR_BAD_ARGUMENTS;
-  return hash_dispatch->get_digest_length(alg, length);
+  return hal_rpc_hash_dispatch->get_digest_length(alg, length);
 }
 
 hal_error_t hal_rpc_hash_get_digest_algorithm_id(const hal_digest_algorithm_t alg,
 						 uint8_t *id, size_t *len, const size_t len_max)
 {
-  return hash_dispatch->get_digest_algorithm_id(alg, id, len, len_max);
+  return hal_rpc_hash_dispatch->get_digest_algorithm_id(alg, id, len, len_max);
 }
 
 hal_error_t hal_rpc_hash_get_algorithm(const hal_hash_handle_t hash, hal_digest_algorithm_t *alg)
 {
   if (hash.handle == hal_hash_handle_none.handle || alg == NULL)
     return HAL_ERROR_BAD_ARGUMENTS;
-  return hash_dispatch->get_algorithm(hash, alg);
+  return hal_rpc_hash_dispatch->get_algorithm(hash, alg);
 }
 
 hal_error_t hal_rpc_hash_initialize(const hal_client_handle_t client,
@@ -189,7 +163,7 @@ hal_error_t hal_rpc_hash_initialize(const hal_client_handle_t client,
 {
   if (hash == NULL)
     return HAL_ERROR_BAD_ARGUMENTS;
-  return hash_dispatch->initialize(client, session, hash, alg, key, key_len);
+  return hal_rpc_hash_dispatch->initialize(client, session, hash, alg, key, key_len);
 }
 
 hal_error_t hal_rpc_hash_update(const hal_hash_handle_t hash,
@@ -199,7 +173,7 @@ hal_error_t hal_rpc_hash_update(const hal_hash_handle_t hash,
     return HAL_ERROR_BAD_ARGUMENTS;
   if (length == 0)
     return HAL_OK;
-  return hash_dispatch->update(hash, data, length);
+  return hal_rpc_hash_dispatch->update(hash, data, length);
 }
 
 hal_error_t hal_rpc_hash_finalize(const hal_hash_handle_t hash,
@@ -207,7 +181,7 @@ hal_error_t hal_rpc_hash_finalize(const hal_hash_handle_t hash,
 {
   if (hash.handle == hal_hash_handle_none.handle || digest == NULL || length == 0)
     return HAL_ERROR_BAD_ARGUMENTS;
-  return hash_dispatch->finalize(hash, digest, length);
+  return hal_rpc_hash_dispatch->finalize(hash, digest, length);
 }
 
 hal_error_t hal_rpc_pkey_load(const hal_client_handle_t client,
@@ -224,7 +198,7 @@ hal_error_t hal_rpc_pkey_load(const hal_client_handle_t client,
       der == NULL || der_len == 0 ||
       !check_pkey_type_curve_flags(type, curve, flags))
     return HAL_ERROR_BAD_ARGUMENTS;
-  return pkey_dispatch->load(client, session, pkey, type, curve, name, name_len, der, der_len, flags);
+  return hal_rpc_pkey_dispatch->load(client, session, pkey, type, curve, name, name_len, der, der_len, flags);
 }
 
 hal_error_t hal_rpc_pkey_find(const hal_client_handle_t client,
@@ -235,7 +209,7 @@ hal_error_t hal_rpc_pkey_find(const hal_client_handle_t client,
 {
   if (pkey == NULL || name == NULL || name_len == 0 || !check_pkey_type(type))
     return HAL_ERROR_BAD_ARGUMENTS;
-  return pkey_dispatch->find(client, session, pkey, type, name, name_len);
+  return hal_rpc_pkey_dispatch->find(client, session, pkey, type, name, name_len);
 }
 
 hal_error_t hal_rpc_pkey_generate_rsa(const hal_client_handle_t client,
@@ -249,7 +223,7 @@ hal_error_t hal_rpc_pkey_generate_rsa(const hal_client_handle_t client,
   if (pkey == NULL || name == NULL || name_len == 0 || key_len == 0 || (key_len & 7) != 0 ||
       exp == NULL || exp_len == 0 || !check_pkey_flags(flags))
     return HAL_ERROR_BAD_ARGUMENTS;
-  return pkey_dispatch->generate_rsa(client, session, pkey, name, name_len, key_len, exp, exp_len, flags);
+  return hal_rpc_pkey_dispatch->generate_rsa(client, session, pkey, name, name_len, key_len, exp, exp_len, flags);
 }
 
 hal_error_t hal_rpc_pkey_generate_ec(const hal_client_handle_t client,
@@ -262,17 +236,17 @@ hal_error_t hal_rpc_pkey_generate_ec(const hal_client_handle_t client,
   if (pkey == NULL || name == NULL || name_len == 0 ||
       !check_pkey_type_curve_flags(HAL_KEY_TYPE_EC_PRIVATE, curve, flags))
     return HAL_ERROR_BAD_ARGUMENTS;
-  return pkey_dispatch->generate_ec(client, session, pkey, name, name_len, curve, flags);
+  return hal_rpc_pkey_dispatch->generate_ec(client, session, pkey, name, name_len, curve, flags);
 }
 
 hal_error_t hal_rpc_pkey_close(const hal_pkey_handle_t pkey)
 {
-  return pkey_dispatch->close(pkey);
+  return hal_rpc_pkey_dispatch->close(pkey);
 }
 
 hal_error_t hal_rpc_pkey_delete(const hal_pkey_handle_t pkey)
 {
-  return pkey_dispatch->delete(pkey);
+  return hal_rpc_pkey_dispatch->delete(pkey);
 }
 
 hal_error_t hal_rpc_pkey_get_key_type(const hal_pkey_handle_t pkey,
@@ -280,7 +254,7 @@ hal_error_t hal_rpc_pkey_get_key_type(const hal_pkey_handle_t pkey,
 {
   if (type == NULL)
     return HAL_ERROR_BAD_ARGUMENTS;
-  return pkey_dispatch->get_key_type(pkey, type);
+  return hal_rpc_pkey_dispatch->get_key_type(pkey, type);
 }
 
 hal_error_t hal_rpc_pkey_get_key_flags(const hal_pkey_handle_t pkey,
@@ -288,12 +262,12 @@ hal_error_t hal_rpc_pkey_get_key_flags(const hal_pkey_handle_t pkey,
 {
   if (flags == NULL)
     return HAL_ERROR_BAD_ARGUMENTS;
-  return pkey_dispatch->get_key_flags(pkey, flags);
+  return hal_rpc_pkey_dispatch->get_key_flags(pkey, flags);
 }
 
 size_t hal_rpc_pkey_get_public_key_len(const hal_pkey_handle_t pkey)
 {
-  return pkey_dispatch->get_public_key_len(pkey);
+  return hal_rpc_pkey_dispatch->get_public_key_len(pkey);
 }
 
 hal_error_t hal_rpc_pkey_get_public_key(const hal_pkey_handle_t pkey,
@@ -301,7 +275,7 @@ hal_error_t hal_rpc_pkey_get_public_key(const hal_pkey_handle_t pkey,
 {
   if (der == NULL || der_len == NULL || der_max == 0)
     return HAL_ERROR_BAD_ARGUMENTS;
-  return pkey_dispatch->get_public_key(pkey, der, der_len, der_max);
+  return hal_rpc_pkey_dispatch->get_public_key(pkey, der, der_len, der_max);
 }
 
 hal_error_t hal_rpc_pkey_sign(const hal_session_handle_t session,
@@ -313,7 +287,7 @@ hal_error_t hal_rpc_pkey_sign(const hal_session_handle_t session,
   if (signature == NULL || signature_len == NULL || signature_max == 0 ||
       (hash.handle == hal_hash_handle_none.handle) == (input == NULL || input_len == 0))
     return HAL_ERROR_BAD_ARGUMENTS;
-  return pkey_dispatch->sign(session, pkey, hash, input,  input_len, signature, signature_len, signature_max);
+  return hal_rpc_pkey_dispatch->sign(session, pkey, hash, input,  input_len, signature, signature_len, signature_max);
 }
 
 hal_error_t hal_rpc_pkey_verify(const hal_session_handle_t session,
@@ -325,7 +299,7 @@ hal_error_t hal_rpc_pkey_verify(const hal_session_handle_t session,
   if (signature == NULL || signature_len == 0 ||
       (hash.handle == hal_hash_handle_none.handle) == (input == NULL || input_len == 0))
     return HAL_ERROR_BAD_ARGUMENTS;
-  return pkey_dispatch->verify(session, pkey, hash, input, input_len, signature, signature_len);
+  return hal_rpc_pkey_dispatch->verify(session, pkey, hash, input, input_len, signature, signature_len);
 }
 
 hal_error_t hal_rpc_pkey_list(hal_pkey_info_t *result,
@@ -334,7 +308,7 @@ hal_error_t hal_rpc_pkey_list(hal_pkey_info_t *result,
 {
   if (result == NULL || result_len == NULL || result_max == 0)
     return HAL_ERROR_BAD_ARGUMENTS;
-  return pkey_dispatch->list(result, result_len, result_max);
+  return hal_rpc_pkey_dispatch->list(result, result_len, result_max);
 }
 
 /*
diff --git a/rpc_client.c b/rpc_client.c
index 3d4fcee..d58cb92 100644
--- a/rpc_client.c
+++ b/rpc_client.c
@@ -3,8 +3,8 @@
  * ------------
  * Remote procedure call client-side private API implementation.
  *
- * Authors: Rob Austein
- * Copyright (c) 2015, NORDUnet A/S All rights reserved.
+ * Authors: Rob Austein, Paul Selkirk
+ * Copyright (c) 2015-2016, 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
@@ -33,25 +33,60 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <assert.h>
+
 #include "hal.h"
 #include "hal_internal.h"
 
 /*
- * RPC calls.  Not implemented yet.
+ * RPC calls.
  */
 
-#warning These are all placeholders, waiting to be filled in with the real RPC calls
+#define check(op) if ((ret = (op)) != HAL_OK) return ret;
+
+#define pad(n) (((n) + 3) & ~3)
 
 static hal_error_t get_random(void *buffer, const size_t length)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[8], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8 + pad(length)], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  uint32_t rcvlen = length;
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_GET_RANDOM));
+  check(rpc_encode_int(&optr, olimit, (uint32_t)length));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    check(rpc_decode_buffer(&iptr, ilimit, buffer, &rcvlen));
+    // XXX check rcvlen vs length
+  }
+  return rpc_ret;
 }
 
 static hal_error_t set_pin(const hal_client_handle_t client,
                            const hal_user_t user,
-                           const char * const newpin, const size_t newpin_len)
+                           const char * const pin, const size_t pin_len)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[12 + pad(pin_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[4], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_SET_PIN));
+  check(rpc_encode_int(&optr, olimit, client.handle));
+  check(rpc_encode_int(&optr, olimit, user));
+  check(rpc_encode_buffer(&optr, olimit, (const uint8_t *)pin, pin_len));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  return rpc_ret;
 }
 
 /*
@@ -72,39 +107,141 @@ static hal_error_t login(const hal_client_handle_t client,
                          const hal_user_t user,
                          const char * const pin, const size_t pin_len)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[12 + pad(pin_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[4], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_LOGIN));
+  check(rpc_encode_int(&optr, olimit, client.handle));
+  check(rpc_encode_int(&optr, olimit, user));
+  check(rpc_encode_buffer(&optr, olimit, (const uint8_t *)pin, pin_len));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  return rpc_ret;
 }
 
 static hal_error_t logout(const hal_client_handle_t client)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[8], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[4], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_LOGOUT));
+  check(rpc_encode_int(&optr, olimit, client.handle));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  return rpc_ret;
 }
 
 static hal_error_t logout_all(void)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[4], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[4], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_LOGOUT_ALL));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  return rpc_ret;
 }
 
 static hal_error_t is_logged_in(const hal_client_handle_t client,
                                 const hal_user_t user)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[12], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[4], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_IS_LOGGED_IN));
+  check(rpc_encode_int(&optr, olimit, client.handle));
+  check(rpc_encode_int(&optr, olimit, user));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  return rpc_ret;
 }
 
 static hal_error_t hash_get_digest_len(const hal_digest_algorithm_t alg, size_t *length)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[8], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  uint32_t len32;
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_HASH_GET_DIGEST_LEN));
+  check(rpc_encode_int(&optr, olimit, alg));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    check(rpc_decode_int(&iptr, ilimit, &len32));
+    *length = (size_t)len32;
+  }
+  return rpc_ret;
 }
 
 static hal_error_t hash_get_digest_algorithm_id(const hal_digest_algorithm_t alg,
                                                 uint8_t *id, size_t *len, const size_t len_max)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[12], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8 + pad(len_max)], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  uint32_t len32 = len_max;
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_HASH_GET_DIGEST_LEN));
+  check(rpc_encode_int(&optr, olimit, alg));
+  check(rpc_encode_int(&optr, olimit, len_max));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    check(rpc_decode_buffer(&iptr, ilimit, id, &len32));
+    *len = len32;
+  }
+  return rpc_ret;
 }
 
 static hal_error_t hash_get_algorithm(const hal_hash_handle_t hash, hal_digest_algorithm_t *alg)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[8], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  uint32_t alg32;
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_HASH_GET_ALGORITHM));
+  check(rpc_encode_int(&optr, olimit, hash.handle));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    check(rpc_decode_int(&iptr, ilimit, &alg32));
+    *alg = (hal_digest_algorithm_t)alg32;
+  }
+  return rpc_ret;
 }
 
 static hal_error_t hash_initialize(const hal_client_handle_t client,
@@ -113,19 +250,68 @@ static hal_error_t hash_initialize(const hal_client_handle_t client,
                                    const hal_digest_algorithm_t alg,
                                    const uint8_t * const key, const size_t key_len)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[20 + pad(key_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_HASH_INITIALIZE));
+  check(rpc_encode_int(&optr, olimit, client.handle));
+  check(rpc_encode_int(&optr, olimit, session.handle));
+  check(rpc_encode_int(&optr, olimit, alg));
+  check(rpc_encode_buffer(&optr, olimit, key, key_len));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    check(rpc_decode_int(&iptr, ilimit, &hash->handle));
+  }
+  return rpc_ret;
 }
 
 static hal_error_t hash_update(const hal_hash_handle_t hash,
                                const uint8_t * data, const size_t length)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[12 + pad(length)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[4], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_HASH_UPDATE));
+  check(rpc_encode_int(&optr, olimit, hash.handle));
+  check(rpc_encode_buffer(&optr, olimit, data, length));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  return rpc_ret;
 }
 
 static hal_error_t hash_finalize(const hal_hash_handle_t hash,
                                  uint8_t *digest, const size_t length)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[12], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8 + pad(length)], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  uint32_t digest_len = length;
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_HASH_FINALIZE));
+  check(rpc_encode_int(&optr, olimit, hash.handle));
+  check(rpc_encode_int(&optr, olimit, length));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    check(rpc_decode_buffer(&iptr, ilimit, digest, &digest_len));
+    /* XXX check digest_len vs length */
+  }
+  return rpc_ret;
 }
 
 static hal_error_t pkey_load(const hal_client_handle_t client,
@@ -137,7 +323,28 @@ static hal_error_t pkey_load(const hal_client_handle_t client,
                              const uint8_t * const der, const size_t der_len,
                              const hal_key_flags_t flags)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[32 + pad(name_len) + pad(der_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_PKEY_LOAD));
+  check(rpc_encode_int(&optr, olimit, client.handle));
+  check(rpc_encode_int(&optr, olimit, session.handle));
+  check(rpc_encode_int(&optr, olimit, type));
+  check(rpc_encode_int(&optr, olimit, curve));
+  check(rpc_encode_buffer(&optr, olimit, name, name_len));
+  check(rpc_encode_buffer(&optr, olimit, der, der_len));
+  check(rpc_encode_int(&optr, olimit, flags));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK)
+    check(rpc_decode_int(&iptr, ilimit, &pkey->handle));
+
+  return rpc_ret;
 }
 
 static hal_error_t pkey_find(const hal_client_handle_t client,
@@ -146,7 +353,25 @@ static hal_error_t pkey_find(const hal_client_handle_t client,
                              const hal_key_type_t type,
                              const uint8_t * const name, const size_t name_len)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[20 + pad(name_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_PKEY_FIND));
+  check(rpc_encode_int(&optr, olimit, client.handle));
+  check(rpc_encode_int(&optr, olimit, session.handle));
+  check(rpc_encode_int(&optr, olimit, type));
+  check(rpc_encode_buffer(&optr, olimit, name, name_len));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK)
+    check(rpc_decode_int(&iptr, ilimit, &pkey->handle));
+
+  return rpc_ret;
 }
 
 static hal_error_t pkey_generate_rsa(const hal_client_handle_t client,
@@ -157,7 +382,27 @@ static hal_error_t pkey_generate_rsa(const hal_client_handle_t client,
                                      const uint8_t * const exp, const size_t exp_len,
                                      const hal_key_flags_t flags)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[28 + pad(name_len) + pad(exp_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_PKEY_GENERATE_RSA));
+  check(rpc_encode_int(&optr, olimit, client.handle));
+  check(rpc_encode_int(&optr, olimit, session.handle));
+  check(rpc_encode_buffer(&optr, olimit, name, name_len));
+  check(rpc_encode_int(&optr, olimit, key_len));
+  check(rpc_encode_buffer(&optr, olimit, exp, exp_len));
+  check(rpc_encode_int(&optr, olimit, flags));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK)
+    check(rpc_decode_int(&iptr, ilimit, &pkey->handle));
+
+  return rpc_ret;
 }
 
 static hal_error_t pkey_generate_ec(const hal_client_handle_t client,
@@ -167,40 +412,153 @@ static hal_error_t pkey_generate_ec(const hal_client_handle_t client,
                                     const hal_curve_name_t curve,
                                     const hal_key_flags_t flags)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[24 + pad(name_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_PKEY_GENERATE_EC));
+  check(rpc_encode_int(&optr, olimit, client.handle));
+  check(rpc_encode_int(&optr, olimit, session.handle));
+  check(rpc_encode_buffer(&optr, olimit, name, name_len));
+  check(rpc_encode_int(&optr, olimit, curve));
+  check(rpc_encode_int(&optr, olimit, flags));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK)
+    check(rpc_decode_int(&iptr, ilimit, &pkey->handle));
+
+  return rpc_ret;
 }
 
 static hal_error_t pkey_close(const hal_pkey_handle_t pkey)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[8], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[4], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_PKEY_CLOSE));
+  check(rpc_encode_int(&optr, olimit, pkey.handle));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  return rpc_ret;
 }
 
 static hal_error_t pkey_delete(const hal_pkey_handle_t pkey)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[8], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[4], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_PKEY_DELETE));
+  check(rpc_encode_int(&optr, olimit, pkey.handle));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  return rpc_ret;
 }
 
 static hal_error_t pkey_get_key_type(const hal_pkey_handle_t pkey,
                                      hal_key_type_t *type)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[8], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  uint32_t type32;
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_PKEY_GET_KEY_TYPE));
+  check(rpc_encode_int(&optr, olimit, pkey.handle));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    check(rpc_decode_int(&iptr, ilimit, &type32));
+    *type = (hal_key_type_t)type32;
+  }
+  return rpc_ret;
 }
 
 static hal_error_t pkey_get_key_flags(const hal_pkey_handle_t pkey,
                                       hal_key_flags_t *flags)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[8], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  uint32_t flags32;
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_PKEY_GET_KEY_FLAGS));
+  check(rpc_encode_int(&optr, olimit, pkey.handle));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    check(rpc_decode_int(&iptr, ilimit, &flags32));
+    *flags = (hal_key_flags_t)flags32;
+  }
+  return rpc_ret;
 }
 
 static size_t pkey_get_public_key_len(const hal_pkey_handle_t pkey)
 {
-  return 0;
+  uint8_t outbuf[8], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  uint32_t len32;
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_PKEY_GET_PUBLIC_KEY_LEN));
+  check(rpc_encode_int(&optr, olimit, pkey.handle));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    check(rpc_decode_int(&iptr, ilimit, &len32));
+    return (size_t)len32;
+  }
+  else
+    return 0;
 }
 
 static hal_error_t pkey_get_public_key(const hal_pkey_handle_t pkey,
                                        uint8_t *der, size_t *der_len, const size_t der_max)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[12], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8 + pad(der_max)], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  uint32_t dlen32 = der_max;
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_PKEY_GET_PUBLIC_KEY));
+  check(rpc_encode_int(&optr, olimit, pkey.handle));
+  check(rpc_encode_int(&optr, olimit, der_max));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    check(rpc_decode_buffer(&iptr, ilimit, der, &dlen32));
+    *der_len = (size_t)dlen32;
+  }
+  return rpc_ret;
 }
 
 static hal_error_t pkey_remote_sign(const hal_session_handle_t session,
@@ -209,7 +567,28 @@ static hal_error_t pkey_remote_sign(const hal_session_handle_t session,
                                     const uint8_t * const input,  const size_t input_len,
                                     uint8_t * signature, size_t *signature_len, const size_t signature_max)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[24 + pad(input_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8 + pad(signature_max)], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  uint32_t slen32 = signature_max;
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_PKEY_REMOTE_SIGN));
+  check(rpc_encode_int(&optr, olimit, session.handle));
+  check(rpc_encode_int(&optr, olimit, pkey.handle));
+  check(rpc_encode_int(&optr, olimit, hash.handle));
+  check(rpc_encode_buffer(&optr, olimit, input, input_len));
+  check(rpc_encode_int(&optr, olimit, signature_max));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    check(rpc_decode_buffer(&iptr, ilimit, signature, &slen32));
+    *signature_len = (size_t)slen32;
+  }
+  return rpc_ret;
 }
 
 static hal_error_t pkey_remote_verify(const hal_session_handle_t session,
@@ -218,14 +597,66 @@ static hal_error_t pkey_remote_verify(const hal_session_handle_t session,
                                       const uint8_t * const input, const size_t input_len,
                                       const uint8_t * const signature, const size_t signature_len)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[24 + pad(input_len) + pad(signature_len)], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[4], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_PKEY_REMOTE_VERIFY));
+  check(rpc_encode_int(&optr, olimit, session.handle));
+  check(rpc_encode_int(&optr, olimit, pkey.handle));
+  check(rpc_encode_int(&optr, olimit, hash.handle));
+  check(rpc_encode_buffer(&optr, olimit, input, input_len));
+  check(rpc_encode_buffer(&optr, olimit, signature, signature_len));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  return rpc_ret;
+}
+
+static hal_error_t rpc_decode_pkey_info(uint8_t **iptr, const uint8_t * const ilimit, hal_pkey_info_t *info)
+{
+  uint32_t i32;
+  hal_error_t ret;
+
+  check(rpc_decode_int(iptr, ilimit, &i32)); info->type = i32;
+  check(rpc_decode_int(iptr, ilimit, &i32)); info->curve = i32;
+  check(rpc_decode_int(iptr, ilimit, &i32)); info->flags = i32;
+  check(rpc_decode_buffer(iptr, ilimit, (uint8_t *)&info->name[0], &i32)); info->name_len = i32;
+  return HAL_OK;
 }
 
 static hal_error_t pkey_list(hal_pkey_info_t *result,
                              unsigned *result_len,
                              const unsigned result_max)
 {
-  return HAL_ERROR_IMPOSSIBLE;
+  uint8_t outbuf[8], *optr = outbuf, *olimit = outbuf + sizeof(outbuf);
+  uint8_t inbuf[8 + pad(result_max * sizeof(hal_pkey_info_t))], *iptr = inbuf, *ilimit = inbuf + sizeof(inbuf);
+  size_t ilen = sizeof(inbuf);
+  uint32_t len;
+  hal_error_t ret, rpc_ret;
+
+  check(rpc_encode_int(&optr, olimit, RPC_FUNC_PKEY_LIST));
+  check(rpc_encode_int(&optr, olimit, result_max));
+  check(rpc_send(outbuf, optr - outbuf));
+
+  check(rpc_recv(inbuf, &ilen));
+  assert(ilen <= sizeof(inbuf));
+  check(rpc_decode_int(&iptr, ilimit, &rpc_ret));
+  if (rpc_ret == HAL_OK) {
+    int i;
+    check(rpc_decode_int(&iptr, ilimit, &len));
+    *result_len = len;
+    for (i = 0; i < len; ++i) {
+      if ((ret = rpc_decode_pkey_info(&iptr, ilimit, &result[i])) != HAL_OK) {
+        *result_len = 0;
+        return ret;
+      }
+    }
+  }
+  return rpc_ret;
 }
 
 
@@ -317,6 +748,40 @@ const hal_rpc_pkey_dispatch_t hal_rpc_mixed_pkey_dispatch = {
   pkey_list
 };
 
+const hal_rpc_misc_dispatch_t * hal_rpc_misc_dispatch;
+const hal_rpc_hash_dispatch_t * hal_rpc_hash_dispatch;
+const hal_rpc_pkey_dispatch_t * hal_rpc_pkey_dispatch;
+
+hal_error_t rpc_client_init(rpc_locality_t locality)
+{
+  switch (locality) {
+  case RPC_LOCAL:
+    hal_rpc_misc_dispatch = &hal_rpc_local_misc_dispatch;
+    hal_rpc_hash_dispatch = &hal_rpc_local_hash_dispatch;
+    hal_rpc_pkey_dispatch = &hal_rpc_local_pkey_dispatch;
+    break; 
+  case RPC_REMOTE:
+    hal_rpc_misc_dispatch = &hal_rpc_remote_misc_dispatch;
+    hal_rpc_hash_dispatch = &hal_rpc_remote_hash_dispatch;
+    hal_rpc_pkey_dispatch = &hal_rpc_remote_pkey_dispatch;
+    break;
+  case RPC_MIXED:
+    hal_rpc_misc_dispatch = &hal_rpc_remote_misc_dispatch;
+    hal_rpc_hash_dispatch = &hal_rpc_remote_hash_dispatch;
+    hal_rpc_pkey_dispatch = &hal_rpc_mixed_pkey_dispatch;
+    break;
+  default:
+    return HAL_ERROR_BAD_ARGUMENTS;
+  }
+  return rpc_client_transport_init();
+}
+
+hal_error_t rpc_client_close(void)
+{
+  return rpc_client_transport_close();
+}
+
+
 /*
  * Local variables:
  * indent-tabs-mode: nil
diff --git a/rpc_client_loopback.c b/rpc_client_loopback.c
new file mode 100644
index 0000000..a3d7e73
--- /dev/null
+++ b/rpc_client_loopback.c
@@ -0,0 +1,85 @@
+/*
+ * rpc_client_loopback.c
+ * ---------------------
+ * Remote procedure call transport over loopback socket.
+ *
+ * Copyright (c) 2016, 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 <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>     /* close */
+
+#include "hal.h"
+#include "hal_internal.h"
+
+static int sock = -1;
+
+hal_error_t rpc_client_transport_init(void)
+{
+    struct sockaddr_in sin;
+
+    sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock == -1)
+        return perror("socket"), HAL_ERROR_RPC_TRANSPORT;
+    sin.sin_family = AF_INET;
+    sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    sin.sin_port = 17425;
+    if (connect(sock, (const struct sockaddr *)&sin, sizeof(sin)) != 0)
+        return perror("connect"), HAL_ERROR_RPC_TRANSPORT;
+    return HAL_OK;
+}
+
+hal_error_t rpc_client_transport_close(void)
+{
+    int ret = close(sock);
+    sock = -1;
+    if (ret != 0)
+        return perror("close"), HAL_ERROR_RPC_TRANSPORT;
+    return HAL_OK;
+}
+
+hal_error_t rpc_send(const uint8_t * const buf, const size_t len)
+{
+    if (send(sock, buf, len, 0) == -1)
+        return perror("send"), HAL_ERROR_RPC_TRANSPORT;
+    return HAL_OK;
+}
+
+hal_error_t rpc_recv(uint8_t * const buf, size_t * const len)
+{
+    int ret;
+    
+    if ((ret = recv(sock, buf, *len, 0)) == -1)
+        return HAL_ERROR_RPC_TRANSPORT;
+    *len = ret;
+    return HAL_OK;
+}
diff --git a/rpc_hash.c b/rpc_hash.c
index df501cf..1bce86e 100644
--- a/rpc_hash.c
+++ b/rpc_hash.c
@@ -4,7 +4,7 @@
  * Remote procedure call server-side hash implementation.
  *
  * Authors: Rob Austein
- * Copyright (c) 2015, NORDUnet A/S All rights reserved.
+ * Copyright (c) 2015-2016, 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
@@ -143,6 +143,12 @@ static inline handle_slot_t *find_handle(const hal_hash_handle_t handle)
   return NULL;
 }
 
+static inline free_handle(handle_slot_t *slot)
+{
+    /* state is a union, so this this works for hash and hmac */
+    slot->state.hash = NULL;
+}
+
 /*
  * Translate an algorithm number to a descriptor.
  */
@@ -234,6 +240,7 @@ static hal_error_t initialize(const hal_client_handle_t client,
 {
   const hal_hash_descriptor_t *descriptor;
   handle_slot_t *slot;
+  hal_error_t err;
 
   if (hash == NULL)
     return HAL_ERROR_BAD_ARGUMENTS;
@@ -241,16 +248,20 @@ static hal_error_t initialize(const hal_client_handle_t client,
   if ((descriptor = alg_to_descriptor(alg)) == NULL)
     return HAL_ERROR_BAD_ARGUMENTS;
 
-  if ((slot = alloc_handle(key != NULL)) == NULL)
+  if ((slot = alloc_handle(key_len != 0)) == NULL)
     return HAL_ERROR_ALLOCATION_FAILURE;
 
   slot->client_handle  = client;
   slot->session_handle = session;
+  *hash = slot->hash_handle;
 
-  if (key == NULL)
-    return hal_hash_initialize(NULL, descriptor, &slot->state.hash, NULL, 0);
+  if (key_len == 0)
+    err = hal_hash_initialize(NULL, descriptor, &slot->state.hash, NULL, 0);
   else
-    return hal_hmac_initialize(NULL, descriptor, &slot->state.hmac, NULL, 0, key, key_len);
+    err = hal_hmac_initialize(NULL, descriptor, &slot->state.hmac, NULL, 0, key, key_len);
+  if (err != HAL_OK)
+    free_handle(slot);
+  return err;
 }
 
 static hal_error_t update(const hal_hash_handle_t handle,
@@ -286,6 +297,7 @@ static hal_error_t finalize(const hal_hash_handle_t handle,
     hal_hmac_cleanup(&slot->state.hmac);
   }
 
+  free_handle(slot);
   return err;
 }
 
diff --git a/rpc_server.c b/rpc_server.c
new file mode 100644
index 0000000..3447c53
--- /dev/null
+++ b/rpc_server.c
@@ -0,0 +1,709 @@
+/*
+ * rpc_server.c
+ * ------------
+ * Remote procedure call server-side private API implementation.
+ *
+ * Copyright (c) 2016, 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 "hal.h"
+#include "hal_internal.h"
+
+/*
+ * RPC calls.
+ */
+
+#define check(op) if ((ret = (op)) != HAL_OK) return ret;
+
+#define pad(n) (((n) + 3) & ~3)
+
+static hal_error_t get_random(uint8_t **iptr, const uint8_t * const ilimit,
+                              uint8_t **optr, const uint8_t * const olimit)
+{
+    uint32_t length;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &length));
+    /* sanity check length */
+    if (length == 0 || length > olimit - *optr - 4)
+        return HAL_ERROR_IO_BAD_COUNT;  /* need a better error */
+
+    /* call the local function */
+    /* get the data directly into the output buffer */
+    check(rpc_encode_int(optr, olimit, length));
+    ret = hal_rpc_local_misc_dispatch.get_random(*optr, (size_t)length);
+    if (ret == HAL_OK)
+        *optr += pad(length);
+    else
+        /* don't return data if error */
+        *optr -= 4;
+
+    return ret;
+}
+
+static hal_error_t set_pin(uint8_t **iptr, const uint8_t * const ilimit,
+                           uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_client_handle_t client;
+    hal_user_t user;
+    uint8_t *pin;
+    uint32_t pin_len;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &client.handle));
+    check(rpc_decode_int(iptr, ilimit, &user));
+    check(rpc_decode_buffer_in_place(iptr, ilimit, &pin, &pin_len));
+
+    /* call the local function */
+    ret = hal_rpc_local_misc_dispatch.set_pin(client, user, (const char * const)pin, pin_len);
+    return ret;
+}
+
+static hal_error_t login(uint8_t **iptr, const uint8_t * const ilimit,
+                         uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_client_handle_t client;
+    hal_user_t user;
+    uint8_t *pin;
+    uint32_t pin_len;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &client.handle));
+    check(rpc_decode_int(iptr, ilimit, &user));
+    check(rpc_decode_buffer_in_place(iptr, ilimit, &pin, &pin_len));
+
+    /* call the local function */
+    ret = hal_rpc_local_misc_dispatch.login(client, user, (const char * const)pin, pin_len);
+    return ret;
+}
+
+static hal_error_t logout(uint8_t **iptr, const uint8_t * const ilimit,
+                          uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_client_handle_t client;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &client.handle));
+
+    /* call the local function */
+    ret = hal_rpc_local_misc_dispatch.logout(client);
+    return ret;
+}
+
+static hal_error_t logout_all(uint8_t **iptr, const uint8_t * const ilimit,
+                              uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_error_t ret;
+
+    /* call the local function */
+    ret = hal_rpc_local_misc_dispatch.logout_all();
+    return ret;
+}
+
+static hal_error_t is_logged_in(uint8_t **iptr, const uint8_t * const ilimit,
+                                uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_client_handle_t client;
+    hal_user_t user;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &client.handle));
+    check(rpc_decode_int(iptr, ilimit, &user));
+
+    /* call the local function */
+    ret = hal_rpc_local_misc_dispatch.is_logged_in(client, user);
+    return ret;
+}
+
+static hal_error_t hash_get_digest_len(uint8_t **iptr, const uint8_t * const ilimit,
+                                       uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_digest_algorithm_t alg;
+    size_t length;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &alg));
+
+    /* call the local function */
+    ret = hal_rpc_local_hash_dispatch.get_digest_length(alg, &length);
+    if (ret == HAL_OK)
+        check(rpc_encode_int(optr, olimit, length));
+    return ret;
+}
+
+static hal_error_t hash_get_digest_algorithm_id(uint8_t **iptr, const uint8_t * const ilimit,
+                                                uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_digest_algorithm_t alg;
+    size_t len;
+    uint32_t len_max;
+    uint8_t *optr_orig = *optr;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &alg));
+    check(rpc_decode_int(iptr, ilimit, &len_max));
+    /* sanity check len_max */
+    if (len_max > olimit - *optr - 4)
+        return HAL_ERROR_IO_BAD_COUNT;  /* need a better error */
+
+    /* call the local function */
+    /* get the data directly into the output buffer */
+    *optr += 4;         /* reserve 4 bytes for length */
+    ret = hal_rpc_local_hash_dispatch.get_digest_algorithm_id(alg, *optr, &len, (size_t)len_max);
+    if (ret == HAL_OK) {
+        *optr = optr_orig;
+        check(rpc_encode_int(optr, olimit, len));
+        *optr += pad(len);
+    }
+    else {
+        /* don't return data if error */
+        *optr = optr_orig;
+    }
+    return ret;
+}
+
+static hal_error_t hash_get_algorithm(uint8_t **iptr, const uint8_t * const ilimit,
+                                      uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_hash_handle_t hash;
+    hal_digest_algorithm_t alg;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &hash.handle));
+
+    /* call the local function */
+    ret = hal_rpc_local_hash_dispatch.get_algorithm(hash, &alg);
+    if (ret == HAL_OK)
+        check(rpc_encode_int(optr, olimit, alg));
+    return ret;
+}
+
+static hal_error_t hash_initialize(uint8_t **iptr, const uint8_t * const ilimit,
+                                   uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_client_handle_t client;
+    hal_session_handle_t session;
+    hal_hash_handle_t hash;
+    uint32_t alg;
+    uint8_t *key;
+    uint32_t key_len;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &client.handle));
+    check(rpc_decode_int(iptr, ilimit, &session.handle));
+    check(rpc_decode_int(iptr, ilimit, &alg));
+    check(rpc_decode_buffer_in_place(iptr, ilimit, &key, &key_len));
+
+    /* call the local function */
+    ret = hal_rpc_local_hash_dispatch.initialize(client, session, &hash, (hal_digest_algorithm_t)alg, key, (size_t)key_len);
+    if (ret == HAL_OK)
+        check(rpc_encode_int(optr, olimit, hash.handle));
+    return ret;
+}
+
+static hal_error_t hash_update(uint8_t **iptr, const uint8_t * const ilimit,
+                               uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_hash_handle_t hash;
+    uint8_t *data;
+    uint32_t length;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &hash.handle));
+    check(rpc_decode_buffer_in_place(iptr, ilimit, &data, &length));
+
+    /* call the local function */
+    ret = hal_rpc_local_hash_dispatch.update(hash, data, (size_t)length);
+    return ret;
+}
+
+static hal_error_t hash_finalize(uint8_t **iptr, const uint8_t * const ilimit,
+                                 uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_hash_handle_t hash;
+    uint32_t length;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &hash.handle));
+    check(rpc_decode_int(iptr, ilimit, &length));
+    /* sanity check length */
+    if (length == 0 || length > olimit - *optr - 4)
+        return HAL_ERROR_IO_BAD_COUNT;  /* need a better error */
+
+    /* call the local function */
+    /* get the data directly into the output buffer */
+    check(rpc_encode_int(optr, olimit, length));
+    ret = hal_rpc_local_hash_dispatch.finalize(hash, *optr, (size_t)length);
+    if (ret == HAL_OK)
+        *optr += pad(length);
+    else
+        /* don't return data if error */
+        *optr -= 4;
+    return ret;
+}
+
+static hal_error_t pkey_load(uint8_t **iptr, const uint8_t * const ilimit,
+                             uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_client_handle_t client;
+    hal_session_handle_t session;
+    hal_pkey_handle_t pkey;
+    hal_key_type_t type;
+    hal_curve_name_t curve;
+    uint8_t *name, *der;
+    uint32_t name_len, der_len;
+    hal_key_flags_t flags;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &client.handle));
+    check(rpc_decode_int(iptr, ilimit, &session.handle));
+    check(rpc_decode_int(iptr, ilimit, &type));
+    check(rpc_decode_int(iptr, ilimit, &curve));
+    check(rpc_decode_buffer_in_place(iptr, ilimit, &name, &name_len));
+    check(rpc_decode_buffer_in_place(iptr, ilimit, &der, &der_len));
+    check(rpc_decode_int(iptr, ilimit, &flags));
+
+    /* call the local function */
+    ret = hal_rpc_local_pkey_dispatch.load(client, session, &pkey, type, curve, name, name_len, der, der_len, flags);
+    if (ret == HAL_OK)
+        check(rpc_encode_int(optr, olimit, pkey.handle));
+    return ret;
+}
+
+static hal_error_t pkey_find(uint8_t **iptr, const uint8_t * const ilimit,
+                             uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_client_handle_t client;
+    hal_session_handle_t session;
+    hal_pkey_handle_t pkey;
+    hal_key_type_t type;
+    uint8_t *name;
+    uint32_t name_len;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &client.handle));
+    check(rpc_decode_int(iptr, ilimit, &session.handle));
+    check(rpc_decode_int(iptr, ilimit, &type));
+    check(rpc_decode_buffer_in_place(iptr, ilimit, &name, &name_len));
+
+    /* call the local function */
+    ret = hal_rpc_local_pkey_dispatch.find(client, session, &pkey, type, name, name_len);
+    if (ret == HAL_OK)
+        check(rpc_encode_int(optr, olimit, pkey.handle));
+    return ret;
+}
+
+static hal_error_t pkey_generate_rsa(uint8_t **iptr, const uint8_t * const ilimit,
+                                     uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_client_handle_t client;
+    hal_session_handle_t session;
+    hal_pkey_handle_t pkey;
+    uint8_t *name;
+    uint32_t name_len;
+    unsigned key_len;
+    uint8_t *exp;
+    uint32_t exp_len;
+    hal_key_flags_t flags;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &client.handle));
+    check(rpc_decode_int(iptr, ilimit, &session.handle));
+    check(rpc_decode_buffer_in_place(iptr, ilimit, &name, &name_len));
+    check(rpc_decode_int(iptr, ilimit, &key_len));
+    check(rpc_decode_buffer_in_place(iptr, ilimit, &exp, &exp_len));
+    check(rpc_decode_int(iptr, ilimit, &flags));
+
+    /* call the local function */
+    ret = hal_rpc_local_pkey_dispatch.generate_rsa(client, session, &pkey, name, name_len, key_len, exp, exp_len, flags);
+    if (ret == HAL_OK)
+        check(rpc_encode_int(optr, olimit, pkey.handle));
+    return ret;
+}
+
+static hal_error_t pkey_generate_ec(uint8_t **iptr, const uint8_t * const ilimit,
+                                    uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_client_handle_t client;
+    hal_session_handle_t session;
+    hal_pkey_handle_t pkey;
+    uint8_t *name;
+    uint32_t name_len;
+    hal_curve_name_t curve;
+    hal_key_flags_t flags;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &client.handle));
+    check(rpc_decode_int(iptr, ilimit, &session.handle));
+    check(rpc_decode_buffer_in_place(iptr, ilimit, &name, &name_len));
+    check(rpc_decode_int(iptr, ilimit, &curve));
+    check(rpc_decode_int(iptr, ilimit, &flags));
+
+    /* call the local function */
+    ret = hal_rpc_local_pkey_dispatch.generate_ec(client, session, &pkey, name, name_len, curve, flags);
+    if (ret == HAL_OK)
+        check(rpc_encode_int(optr, olimit, pkey.handle));
+    return ret;
+}
+
+static hal_error_t pkey_close(uint8_t **iptr, const uint8_t * const ilimit,
+                              uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_pkey_handle_t pkey;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &pkey.handle));
+
+    /* call the local function */
+    ret = hal_rpc_local_pkey_dispatch.close(pkey);
+    return ret;
+}
+
+static hal_error_t pkey_delete(uint8_t **iptr, const uint8_t * const ilimit,
+                               uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_pkey_handle_t pkey;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &pkey.handle));
+
+    /* call the local function */
+    ret = hal_rpc_local_pkey_dispatch.delete(pkey);
+    return ret;
+}
+
+static hal_error_t pkey_get_key_type(uint8_t **iptr, const uint8_t * const ilimit,
+                                     uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_pkey_handle_t pkey;
+    hal_key_type_t type;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &pkey.handle));
+
+    /* call the local function */
+    ret = hal_rpc_local_pkey_dispatch.get_key_type(pkey, &type);
+    if (ret == HAL_OK)
+        check(rpc_encode_int(optr, olimit, type));
+    return ret;
+}
+
+static hal_error_t pkey_get_key_flags(uint8_t **iptr, const uint8_t * const ilimit,
+                                      uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_pkey_handle_t pkey;
+    hal_key_flags_t flags;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &pkey.handle));
+
+    /* call the local function */
+    ret = hal_rpc_local_pkey_dispatch.get_key_flags(pkey, &flags);
+    if (ret == HAL_OK)
+        check(rpc_encode_int(optr, olimit, flags));
+    return ret;
+}
+
+static hal_error_t pkey_get_public_key_len(uint8_t **iptr, const uint8_t * const ilimit,
+                                           uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_pkey_handle_t pkey;
+    size_t len;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &pkey.handle));
+
+    /* call the local function */
+    len = hal_rpc_local_pkey_dispatch.get_public_key_len(pkey);
+    check(rpc_encode_int(optr, olimit, len));
+    return HAL_OK;
+}
+
+static hal_error_t pkey_get_public_key(uint8_t **iptr, const uint8_t * const ilimit,
+                                       uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_pkey_handle_t pkey;
+    size_t len;
+    uint32_t len_max;
+    uint8_t *optr_orig = *optr;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &pkey.handle));
+    check(rpc_decode_int(iptr, ilimit, &len_max));
+    /* sanity check len_max */
+    if (len_max > olimit - *optr - 4)
+        return HAL_ERROR_IO_BAD_COUNT;  /* need a better error */
+
+    /* call the local function */
+    /* get the data directly into the output buffer */
+    *optr += 4;         /* reserve 4 bytes for length */
+    ret = hal_rpc_local_pkey_dispatch.get_public_key(pkey, *optr, &len, len_max);
+    if (ret == HAL_OK) {
+        *optr = optr_orig;
+        check(rpc_encode_int(optr, olimit, len));
+        *optr += pad(len);
+    }
+    else {
+        /* don't return data if error */
+        *optr = optr_orig;
+    }
+    return ret;
+}
+
+static hal_error_t pkey_remote_sign(uint8_t **iptr, const uint8_t * const ilimit,
+                                    uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_session_handle_t session;
+    hal_pkey_handle_t pkey;
+    hal_hash_handle_t hash;
+    uint8_t *input;
+    uint32_t input_len;
+    uint32_t sig_max;
+    size_t sig_len;
+    uint8_t *optr_orig = *optr;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &session.handle));
+    check(rpc_decode_int(iptr, ilimit, &pkey.handle));
+    check(rpc_decode_int(iptr, ilimit, &hash.handle));
+    check(rpc_decode_buffer_in_place(iptr, ilimit, &input, &input_len));
+    check(rpc_decode_int(iptr, ilimit, &sig_max));
+    /* sanity check sig_max */
+    if (sig_max > olimit - *optr - 4)
+        return HAL_ERROR_IO_BAD_COUNT;  /* need a better error */
+
+    /* call the local function */
+    /* get the data directly into the output buffer */
+    *optr += 4;         /* reserve 4 bytes for length */
+    ret = hal_rpc_local_pkey_dispatch.sign(session, pkey, hash, input, input_len, *optr, &sig_len, sig_max);
+    *optr = optr_orig;
+    if (ret == HAL_OK) {
+        check(rpc_encode_int(optr, olimit, sig_len));
+        *optr += pad(sig_len);
+    }
+    return ret;
+}
+
+static hal_error_t pkey_remote_verify(uint8_t **iptr, const uint8_t * const ilimit,
+                                      uint8_t **optr, const uint8_t * const olimit)
+{
+    hal_session_handle_t session;
+    hal_pkey_handle_t pkey;
+    hal_hash_handle_t hash;
+    uint8_t *input;
+    uint32_t input_len;
+    uint8_t *sig;
+    uint32_t sig_len;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &session.handle));
+    check(rpc_decode_int(iptr, ilimit, &pkey.handle));
+    check(rpc_decode_int(iptr, ilimit, &hash.handle));
+    check(rpc_decode_buffer_in_place(iptr, ilimit, &input, &input_len));
+    check(rpc_decode_buffer_in_place(iptr, ilimit, &sig, &sig_len));
+
+    /* call the local function */
+    ret = hal_rpc_local_pkey_dispatch.verify(session, pkey, hash, input, input_len, sig, sig_len);
+    return ret;
+}
+
+static hal_error_t rpc_encode_pkey_info(uint8_t **optr, const uint8_t * const olimit, const hal_pkey_info_t *info)
+{
+    uint8_t *optr_orig = *optr;
+    hal_error_t ret;
+    
+    if ((ret = rpc_encode_int(optr, olimit, info->type)) != HAL_OK ||
+        (ret = rpc_encode_int(optr, olimit, info->curve)) != HAL_OK ||
+        (ret = rpc_encode_int(optr, olimit, info->flags)) != HAL_OK ||
+        (ret = rpc_encode_buffer(optr, olimit, (uint8_t *)&info->name[0], info->name_len)) != HAL_OK)
+        *optr = optr_orig;
+    return ret;
+}
+
+
+static hal_error_t pkey_list(uint8_t **iptr, const uint8_t * const ilimit,
+                             uint8_t **optr, const uint8_t * const olimit)
+{
+    uint8_t *optr_orig = *optr;
+    uint32_t result_max;
+    hal_error_t ret;
+
+    check(rpc_decode_int(iptr, ilimit, &result_max));
+
+    hal_pkey_info_t result[result_max];
+    unsigned result_len;
+
+    /* call the local function */
+    ret = hal_rpc_local_pkey_dispatch.list(result, &result_len, result_max);
+    if (ret == HAL_OK) {
+        int i;
+        check(rpc_encode_int(optr, olimit, result_len));
+        for (i = 0; i < result_len; ++i) {
+            if ((ret = rpc_encode_pkey_info(optr, olimit, &result[i])) != HAL_OK) {
+                *optr = optr_orig;
+                break;
+            }
+        }
+    }
+    return ret;
+}
+
+#define MAX_PKT_SIZE 1024
+#define interrupt 0
+
+void rpc_server_main(void)
+{
+    uint8_t inbuf[MAX_PKT_SIZE], outbuf[MAX_PKT_SIZE];
+    uint8_t *iptr, *ilimit, *optr, *olimit;
+    size_t ilen, olen;
+    rpc_func_num_t rpc_func_num;
+    void *opaque;
+    hal_error_t ret;
+    
+    while (!interrupt) {
+        ilen = sizeof(inbuf);
+        if (rpc_recvfrom(inbuf, &ilen, &opaque) == HAL_OK) {
+            iptr = inbuf;
+            ilimit = inbuf + ilen;
+            optr = outbuf + 4;  /* reserve 4 bytes for return code */
+            olimit = outbuf + sizeof(outbuf);
+            rpc_decode_int(&iptr, ilimit, &rpc_func_num);
+            switch (rpc_func_num) {
+            case RPC_FUNC_GET_RANDOM:
+                ret = get_random(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_SET_PIN:
+                ret = set_pin(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_LOGIN:
+                ret = login(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_LOGOUT:
+                ret = logout(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_LOGOUT_ALL:
+                ret = logout_all(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_IS_LOGGED_IN:
+                ret = is_logged_in(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_HASH_GET_DIGEST_LEN:
+                ret = hash_get_digest_len(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_HASH_GET_DIGEST_ALGORITHM_ID:
+                ret = hash_get_digest_algorithm_id(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_HASH_GET_ALGORITHM:
+                ret = hash_get_algorithm(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_HASH_INITIALIZE:
+                ret = hash_initialize(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_HASH_UPDATE:
+                ret = hash_update(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_HASH_FINALIZE:
+                ret = hash_finalize(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_PKEY_LOAD:
+                ret = pkey_load(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_PKEY_FIND:
+                ret = pkey_find(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_PKEY_GENERATE_RSA:
+                ret = pkey_generate_rsa(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_PKEY_GENERATE_EC:
+                ret = pkey_generate_ec(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_PKEY_CLOSE:
+                ret = pkey_close(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_PKEY_DELETE:
+                ret = pkey_delete(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_PKEY_GET_KEY_TYPE:
+                ret = pkey_get_key_type(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_PKEY_GET_KEY_FLAGS:
+                ret = pkey_get_key_flags(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_PKEY_GET_PUBLIC_KEY_LEN:
+                ret = pkey_get_public_key_len(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_PKEY_GET_PUBLIC_KEY:
+                ret = pkey_get_public_key(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_PKEY_REMOTE_SIGN:
+                ret = pkey_remote_sign(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_PKEY_REMOTE_VERIFY:
+                ret = pkey_remote_verify(&iptr, ilimit, &optr, olimit);
+                break;
+            case RPC_FUNC_PKEY_LIST:
+                ret = pkey_list(&iptr, ilimit, &optr, olimit);
+                break;
+            default:
+                ret = HAL_ERROR_BAD_ARGUMENTS;  /* need a better error */
+                break;
+            }
+            /* encode the return code at the beginning of the payload */
+            olen = optr - outbuf;
+            optr = outbuf;
+            rpc_encode_int(&optr, olimit, ret);
+            rpc_sendto(outbuf, olen, opaque);
+        }
+    }
+}
+
+/*
+ * Dispatch vectors.
+ */
+
+const hal_rpc_misc_dispatch_t *hal_rpc_misc_dispatch = &hal_rpc_local_misc_dispatch;
+const hal_rpc_hash_dispatch_t *hal_rpc_hash_dispatch = &hal_rpc_local_hash_dispatch;
+const hal_rpc_pkey_dispatch_t *hal_rpc_pkey_dispatch = &hal_rpc_local_pkey_dispatch;
+
+hal_error_t rpc_server_init(void)
+{
+    return rpc_server_transport_init();
+}
+
+hal_error_t rpc_server_close(void)
+{
+    return rpc_server_transport_close();
+}
+
+
+/*
+ * Local variables:
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/rpc_server_loopback.c b/rpc_server_loopback.c
new file mode 100644
index 0000000..a39e12b
--- /dev/null
+++ b/rpc_server_loopback.c
@@ -0,0 +1,89 @@
+/*
+ * rpc_server_loopback.c
+ * ---------------------
+ * Remote procedure call transport over loopback socket.
+ *
+ * Copyright (c) 2016, 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 <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>	/* close */
+
+#include "hal.h"
+#include "hal_internal.h"
+
+static int sock;
+
+hal_error_t rpc_server_transport_init(void)
+{
+    struct sockaddr_in sin;
+
+    sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock == -1)
+	return perror("socket"), HAL_ERROR_RPC_TRANSPORT;
+    sin.sin_family = AF_INET;
+    sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+    sin.sin_port = 17425;
+    if (bind(sock, (const struct sockaddr *)&sin, sizeof(sin)) != 0)
+	return perror("bind"), HAL_ERROR_RPC_TRANSPORT;
+    return HAL_OK;
+}
+
+hal_error_t rpc_server_transport_close(void)
+{
+    if (close(sock) != 0)
+	return perror("close"), HAL_ERROR_RPC_TRANSPORT;
+    return HAL_OK;
+}
+
+hal_error_t rpc_sendto(const uint8_t * const buf, const size_t len, void *opaque)
+{
+    struct sockaddr_in *sin = (struct sockaddr_in *)opaque;
+    int ret;
+
+    if ((ret = sendto(sock, buf, len, 0, (struct sockaddr *)sin, sizeof(*sin))) == -1)
+	return perror("sendto"), HAL_ERROR_RPC_TRANSPORT;
+    return HAL_OK;
+}
+
+hal_error_t rpc_recvfrom(uint8_t * const buf, size_t * const len, void **opaque)
+{
+    static struct sockaddr_in sin;
+    socklen_t sin_len = sizeof(sin);
+    int ret;
+    
+    if ((ret = recvfrom(sock, buf, *len, 0, (struct sockaddr *)&sin, &sin_len)) == -1)
+	return HAL_ERROR_RPC_TRANSPORT;
+    *opaque = (void *)&sin;
+    *len = ret;
+    return HAL_OK;
+}
diff --git a/rpc_xdr.c b/rpc_xdr.c
new file mode 100644
index 0000000..69b8482
--- /dev/null
+++ b/rpc_xdr.c
@@ -0,0 +1,245 @@
+/*
+ * rpc_xdr.c
+ * ---------
+ * Serialization/deserialization routines, using XDR (RFC 4506) encoding.
+ *
+ * Copyright (c) 2016, 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 <stdio.h>
+#include <stdint.h>
+#include <string.h>		/* memcpy */
+#include <strings.h>		/* bzero */
+#include <arpa/inet.h>		/* htonl/ntohl */
+
+#include "hal.h"
+
+/* encode/decode_int. This covers int, unsigned int, enum, and bool types,
+ * which are all encoded as 32-bit big-endian fields. Signed integers are
+ * defined to use two's complement, but that's universal these days, yes?
+ */
+
+hal_error_t rpc_encode_int(uint8_t ** const outbuf, const uint8_t * const limit, const uint32_t value)
+{
+    /* arg checks */
+    if (outbuf == NULL || *outbuf == NULL || limit == NULL)
+	return HAL_ERROR_BAD_ARGUMENTS;
+
+    /* buffer overflow check */
+    if (limit - *outbuf < sizeof(value))
+	return HAL_ERROR_IO_BAD_COUNT;
+
+    **(uint32_t **)outbuf = htonl(value);
+    *outbuf += sizeof(value);
+    return HAL_OK;
+}
+
+hal_error_t rpc_decode_int(uint8_t **inbuf, const uint8_t * const limit, uint32_t *value)
+{
+    /* arg checks */
+    if (inbuf == NULL || *inbuf == NULL || limit == NULL || value == NULL)
+	return HAL_ERROR_BAD_ARGUMENTS;
+
+    /* buffer overflow check */
+    if (limit - *inbuf < sizeof(*value))
+	return HAL_ERROR_IO_BAD_COUNT;
+
+    *value = ntohl(**(uint32_t **)inbuf);
+    *inbuf += sizeof(*value);
+    return HAL_OK;
+}
+
+/* encode/decode_buffer. This covers variable-length string and opaque types.
+ * The data is preceded by a 4-byte length word (encoded as above), and padded
+ * to a multiple of 4 bytes as necessary.
+ */
+
+hal_error_t rpc_encode_buffer(uint8_t **outbuf, const uint8_t * const limit, const uint8_t *value, const uint32_t len)
+{
+    hal_error_t ret;
+
+    /* arg checks */
+    if (outbuf == NULL || *outbuf == NULL || limit == NULL || 
+	(value == NULL && len != 0))
+	return HAL_ERROR_BAD_ARGUMENTS;
+
+    /* buffer overflow check */
+    if ((limit - *outbuf) < (((len + 3) & ~3) + sizeof(len)))
+	return HAL_ERROR_IO_BAD_COUNT;
+
+    /* encode length */
+    if ((ret = rpc_encode_int(outbuf, limit, len)) != HAL_OK)
+	return ret;
+
+    /* write the string or opaque data */
+    memcpy(*outbuf, value, len);
+    *outbuf += len;
+
+    /* pad if necessary */
+    if (len & 3) {
+	size_t n = 4 - (len & 3);
+	bzero(*outbuf, n);
+	*outbuf += n;
+    }
+
+    return HAL_OK;
+}
+
+/* This version returns a pointer to the data in the input buffer.
+ * It is used in the rpc server.
+ */
+hal_error_t rpc_decode_buffer_in_place(uint8_t **inbuf, const uint8_t * const limit, uint8_t ** const value, uint32_t * const len)
+{
+    hal_error_t ret;
+    uint32_t xdr_len;
+    uint8_t *orig_inbuf = *inbuf;
+
+    /* arg checks */
+    if (inbuf == NULL || *inbuf == NULL || limit == NULL || value == NULL || len == NULL)
+	return HAL_ERROR_BAD_ARGUMENTS;
+
+    /* decode the length */
+    if ((ret = rpc_decode_int(inbuf, limit, &xdr_len)) != HAL_OK)
+	return ret;
+
+    /* input and output buffer overflow checks vs decoded length */
+
+    /* decoded length is past the end of the input buffer;
+     * we're probably out of sync, but nothing we can do now
+     */
+    if (limit - *inbuf < xdr_len) {
+	/* undo read of length */
+	*inbuf = orig_inbuf;
+	return HAL_ERROR_IO_BAD_COUNT;
+    }
+
+    /* user buffer is too small, update *len
+     */
+    if (*len < xdr_len) {
+	*len = xdr_len;
+	/* undo read of length */
+	*inbuf = orig_inbuf;
+	return HAL_ERROR_IO_BAD_COUNT;
+    }
+
+    /* return a pointer to the string or opaque data */
+    *value = *inbuf;
+    *len = xdr_len;
+
+    /* update the buffer pointer, skipping any padding bytes */
+    *inbuf += (xdr_len + 3) & ~3;
+
+    return HAL_OK;
+}
+
+/* This version copies the data to the user-supplied buffer.
+ * It is used in the rpc client.
+ */
+hal_error_t rpc_decode_buffer(uint8_t **inbuf, const uint8_t * const limit, uint8_t * const value, uint32_t * const len)
+{
+    hal_error_t ret;
+    uint8_t *vptr;
+
+    if ((ret = rpc_decode_buffer_in_place(inbuf, limit, &vptr, len)) == HAL_OK)
+	memcpy(value, vptr, *len);
+    return ret;
+}
+
+#ifdef TEST
+void hexdump(uint8_t *buf, uint32_t len)
+{
+    int i;
+
+    for (i = 0; i < len; ++i) {
+	uint8_t c = buf[i];
+	printf("%02x ", c);
+	if ((i & 0x07) == 0x07)
+	    printf("\n");
+    }
+    if ((len & 0x07) != 0)
+	printf("\n");
+}
+
+int main(int argc, char *argv[])
+{
+    uint32_t i;
+    uint8_t buf[64] = {0};
+    uint8_t *bufptr = buf, *readptr;
+    uint8_t *limit = buf + sizeof(buf);
+    hal_error_t ret;
+    uint8_t alphabet[] = "abcdefghijklmnopqrstuvwxyz";
+    uint8_t readbuf[64] = {0};
+
+    printf("rpc_encode_int: work to failure\n");
+    for (i = 1; i < 100; ++i) {
+	if ((ret = rpc_encode_int(&bufptr, limit, i)) != HAL_OK) {
+	    printf("%d: %s\n", i, hal_error_string(ret));
+	    break;
+	}
+    }
+    hexdump(buf, ((uint8_t *)bufptr - buf));
+
+    printf("\nrpc_decode_int:\n");
+    readptr = buf;
+    while (readptr < bufptr) {
+	if ((ret = rpc_decode_int(&readptr, limit, &i)) != HAL_OK) {
+	    printf("%s\n", hal_error_string(ret));
+	    break;
+	}
+	printf("%u ", i);
+    }
+    printf("\n");
+
+    printf("\nrpc_encode_buffer: work to failure\n");
+    bzero(buf, sizeof(buf));
+    bufptr = buf;
+     for (i = 1; i < 10; ++i) {
+	if ((ret = rpc_encode_buffer(&bufptr, limit, alphabet, i)) != HAL_OK) {
+	    printf("%d: %s\n", i, hal_error_string(ret));
+	    break;
+	}
+    }
+    hexdump(buf, ((uint8_t *)bufptr - buf));
+
+    printf("\nrpc_decode_buffer:\n");
+    readptr = buf;
+    i = sizeof(readbuf);
+    while (readptr < bufptr) {
+	if ((ret = rpc_decode_buffer(&readptr, limit, readbuf, &i)) != HAL_OK) {
+	    printf("%s\n", hal_error_string(ret));
+	    break;
+	}
+	printf("%u: ", i); for (int j = 0; j < i; ++j) putchar(readbuf[j]); putchar('\n');
+	i = sizeof(readbuf);
+	bzero(readbuf, sizeof(readbuf));
+    }
+
+    return 0;
+}
+#endif
diff --git a/tests/GNUmakefile b/tests/GNUmakefile
index ba515f7..2f026a1 100644
--- a/tests/GNUmakefile
+++ b/tests/GNUmakefile
@@ -30,6 +30,7 @@
 INC	= ../hal.h
 LIB	= ../libhal.a
 BIN	= test-aes-key-wrap test-hash test-pbkdf2 test-ecdsa test-bus test-trng test-rsa
+BIN	+= test-rpc_hash test-rpc_server
 
 CFLAGS	= -g3 -Wall -fPIC -std=c99 -I..
 
diff --git a/tests/test-rpc_hash.c b/tests/test-rpc_hash.c
new file mode 100644
index 0000000..d59e341
--- /dev/null
+++ b/tests/test-rpc_hash.c
@@ -0,0 +1,698 @@
+/*
+ * test-rpc_hash.c
+ * ---------------
+ * Test code for RPC interface to Cryptech hash cores.
+ *
+ * Authors: Rob Austein, Paul Selkirk
+ * Copyright (c) 2015-2016, 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 <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include <hal.h>
+#include <hal_internal.h>
+
+/* Usual NIST sample messages. */
+
+/* "abc" */
+static const uint8_t nist_512_single[] = { /* 3 bytes */
+  0x61, 0x62, 0x63
+};
+
+static const uint8_t sha1_single_digest[] = { /* 20 bytes */
+  0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e, 0x25, 0x71,
+  0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d
+};
+
+static const uint8_t sha256_single_digest[] = { /* 32 bytes */
+  0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde,
+  0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+  0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
+};
+
+/* "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" */
+static const uint8_t nist_512_double[] = { /* 56 bytes */
+  0x61, 0x62, 0x63, 0x64, 0x62, 0x63, 0x64, 0x65, 0x63, 0x64, 0x65, 0x66,
+  0x64, 0x65, 0x66, 0x67, 0x65, 0x66, 0x67, 0x68, 0x66, 0x67, 0x68, 0x69,
+  0x67, 0x68, 0x69, 0x6a, 0x68, 0x69, 0x6a, 0x6b, 0x69, 0x6a, 0x6b, 0x6c,
+  0x6a, 0x6b, 0x6c, 0x6d, 0x6b, 0x6c, 0x6d, 0x6e, 0x6c, 0x6d, 0x6e, 0x6f,
+  0x6d, 0x6e, 0x6f, 0x70, 0x6e, 0x6f, 0x70, 0x71
+};
+
+static const uint8_t sha1_double_digest[] = { /* 20 bytes */
+  0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae, 0x4a, 0xa1,
+  0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1
+};
+
+static const uint8_t sha256_double_digest[] = { /* 32 bytes */
+  0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, 0x93,
+  0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
+  0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1
+};
+
+/* "abc" */
+static const uint8_t nist_1024_single[] = { /* 3 bytes */
+  0x61, 0x62, 0x63
+};
+
+static const uint8_t sha512_224_single_digest[] = { /* 28 bytes */
+  0x46, 0x34, 0x27, 0x0f, 0x70, 0x7b, 0x6a, 0x54, 0xda, 0xae, 0x75, 0x30,
+  0x46, 0x08, 0x42, 0xe2, 0x0e, 0x37, 0xed, 0x26, 0x5c, 0xee, 0xe9, 0xa4,
+  0x3e, 0x89, 0x24, 0xaa
+};
+
+static const uint8_t sha512_256_single_digest[] = { /* 32 bytes */
+  0x53, 0x04, 0x8e, 0x26, 0x81, 0x94, 0x1e, 0xf9, 0x9b, 0x2e, 0x29, 0xb7,
+  0x6b, 0x4c, 0x7d, 0xab, 0xe4, 0xc2, 0xd0, 0xc6, 0x34, 0xfc, 0x6d, 0x46,
+  0xe0, 0xe2, 0xf1, 0x31, 0x07, 0xe7, 0xaf, 0x23
+};
+
+static const uint8_t sha384_single_digest[] = { /* 48 bytes */
+  0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, 0xa0, 0x3d, 0x69,
+  0x9a, 0xc6, 0x50, 0x07, 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63,
+  0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b,
+  0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7
+};
+
+static const uint8_t sha512_single_digest[] = { /* 64 bytes */
+  0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49,
+  0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2,
+  0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a,
+  0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd,
+  0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f,
+  0xa5, 0x4c, 0xa4, 0x9f
+};
+
+/* "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
+   "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" */
+static const uint8_t nist_1024_double[] = { /* 112 bytes */
+  0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x62, 0x63, 0x64, 0x65,
+  0x66, 0x67, 0x68, 0x69, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+  0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x65, 0x66, 0x67, 0x68,
+  0x69, 0x6a, 0x6b, 0x6c, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
+  0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x68, 0x69, 0x6a, 0x6b,
+  0x6c, 0x6d, 0x6e, 0x6f, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+  0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x6b, 0x6c, 0x6d, 0x6e,
+  0x6f, 0x70, 0x71, 0x72, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
+  0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x6e, 0x6f, 0x70, 0x71,
+  0x72, 0x73, 0x74, 0x75
+};
+
+static const uint8_t sha512_224_double_digest[] = { /* 28 bytes */
+  0x23, 0xfe, 0xc5, 0xbb, 0x94, 0xd6, 0x0b, 0x23, 0x30, 0x81, 0x92, 0x64,
+  0x0b, 0x0c, 0x45, 0x33, 0x35, 0xd6, 0x64, 0x73, 0x4f, 0xe4, 0x0e, 0x72,
+  0x68, 0x67, 0x4a, 0xf9
+};
+
+static const uint8_t sha512_256_double_digest[] = { /* 32 bytes */
+  0x39, 0x28, 0xe1, 0x84, 0xfb, 0x86, 0x90, 0xf8, 0x40, 0xda, 0x39, 0x88,
+  0x12, 0x1d, 0x31, 0xbe, 0x65, 0xcb, 0x9d, 0x3e, 0xf8, 0x3e, 0xe6, 0x14,
+  0x6f, 0xea, 0xc8, 0x61, 0xe1, 0x9b, 0x56, 0x3a
+};
+
+static const uint8_t sha384_double_digest[] = { /* 48 bytes */
+  0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8, 0x3d, 0x19, 0x2f, 0xc7,
+  0x82, 0xcd, 0x1b, 0x47, 0x53, 0x11, 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2,
+  0x2f, 0xa0, 0x80, 0x86, 0xe3, 0xb0, 0xf7, 0x12, 0xfc, 0xc7, 0xc7, 0x1a,
+  0x55, 0x7e, 0x2d, 0xb9, 0x66, 0xc3, 0xe9, 0xfa, 0x91, 0x74, 0x60, 0x39
+};
+
+static const uint8_t sha512_double_digest[] = { /* 64 bytes */
+  0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda, 0x8c, 0xf4, 0xf7, 0x28,
+  0x14, 0xfc, 0x14, 0x3f, 0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1,
+  0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18, 0x50, 0x1d, 0x28, 0x9e,
+  0x49, 0x00, 0xf7, 0xe4, 0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a,
+  0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54, 0x5e, 0x96, 0xe5, 0x5b,
+  0x87, 0x4b, 0xe9, 0x09
+};
+
+/* HMAC-SHA-1 test cases from RFC 2202. */
+
+static const uint8_t hmac_sha1_tc_1_key[] = { /* 20 bytes */
+  0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+  0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+};
+
+/* 'Hi There' */
+static const uint8_t hmac_sha1_tc_1_data[] = { /* 8 bytes */
+  0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65
+};
+
+static const uint8_t hmac_sha1_tc_1_result_sha1[] = { /* 20 bytes */
+  0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xe2, 0x8b, 0xc0, 0xb6,
+  0xfb, 0x37, 0x8c, 0x8e, 0xf1, 0x46, 0xbe, 0x00
+};
+
+/* 'Jefe' */
+static const uint8_t hmac_sha1_tc_2_key[] = { /* 4 bytes */
+  0x4a, 0x65, 0x66, 0x65
+};
+
+/* 'what do ya want for nothing?' */
+static const uint8_t hmac_sha1_tc_2_data[] = { /* 28 bytes */
+  0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20, 0x79, 0x61, 0x20, 0x77,
+  0x61, 0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+  0x69, 0x6e, 0x67, 0x3f
+};
+
+static const uint8_t hmac_sha1_tc_2_result_sha1[] = { /* 20 bytes */
+  0xef, 0xfc, 0xdf, 0x6a, 0xe5, 0xeb, 0x2f, 0xa2, 0xd2, 0x74, 0x16, 0xd5,
+  0xf1, 0x84, 0xdf, 0x9c, 0x25, 0x9a, 0x7c, 0x79
+};
+
+static const uint8_t hmac_sha1_tc_3_key[] = { /* 20 bytes */
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+static const uint8_t hmac_sha1_tc_3_data[] = { /* 50 bytes */
+  0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+  0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+  0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+  0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+  0xdd, 0xdd
+};
+
+static const uint8_t hmac_sha1_tc_3_result_sha1[] = { /* 20 bytes */
+  0x12, 0x5d, 0x73, 0x42, 0xb9, 0xac, 0x11, 0xcd, 0x91, 0xa3, 0x9a, 0xf4,
+  0x8a, 0xa1, 0x7b, 0x4f, 0x63, 0xf1, 0x75, 0xd3
+};
+
+static const uint8_t hmac_sha1_tc_4_key[] = { /* 25 bytes */
+  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+  0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19
+};
+
+static const uint8_t hmac_sha1_tc_4_data[] = { /* 50 bytes */
+  0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+  0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+  0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+  0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+  0xcd, 0xcd
+};
+
+static const uint8_t hmac_sha1_tc_4_result_sha1[] = { /* 20 bytes */
+  0x4c, 0x90, 0x07, 0xf4, 0x02, 0x62, 0x50, 0xc6, 0xbc, 0x84, 0x14, 0xf9,
+  0xbf, 0x50, 0xc8, 0x6c, 0x2d, 0x72, 0x35, 0xda
+};
+
+static const uint8_t hmac_sha1_tc_5_key[] = { /* 20 bytes */
+  0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+  0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c
+};
+
+/* 'Test With Truncation' */
+static const uint8_t hmac_sha1_tc_5_data[] = { /* 20 bytes */
+  0x54, 0x65, 0x73, 0x74, 0x20, 0x57, 0x69, 0x74, 0x68, 0x20, 0x54, 0x72,
+  0x75, 0x6e, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e
+};
+
+static const uint8_t hmac_sha1_tc_5_result_sha1[] = { /* 20 bytes */
+  0x4c, 0x1a, 0x03, 0x42, 0x4b, 0x55, 0xe0, 0x7f, 0xe7, 0xf2, 0x7b, 0xe1,
+  0xd5, 0x8b, 0xb9, 0x32, 0x4a, 0x9a, 0x5a, 0x04
+};
+
+static const uint8_t hmac_sha1_tc_6_key[] = { /* 80 bytes */
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+/* 'Test Using Larger Than Block-Size Key - Hash Key First' */
+static const uint8_t hmac_sha1_tc_6_data[] = { /* 54 bytes */
+  0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x4c,
+  0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x54, 0x68, 0x61, 0x6e, 0x20, 0x42,
+  0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x53, 0x69, 0x7a, 0x65, 0x20, 0x4b, 0x65,
+  0x79, 0x20, 0x2d, 0x20, 0x48, 0x61, 0x73, 0x68, 0x20, 0x4b, 0x65, 0x79,
+  0x20, 0x46, 0x69, 0x72, 0x73, 0x74
+};
+
+static const uint8_t hmac_sha1_tc_6_result_sha1[] = { /* 20 bytes */
+  0xaa, 0x4a, 0xe5, 0xe1, 0x52, 0x72, 0xd0, 0x0e, 0x95, 0x70, 0x56, 0x37,
+  0xce, 0x8a, 0x3b, 0x55, 0xed, 0x40, 0x21, 0x12
+};
+
+static const uint8_t hmac_sha1_tc_7_key[] = { /* 80 bytes */
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+/* 'Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data' */
+static const uint8_t hmac_sha1_tc_7_data[] = { /* 73 bytes */
+  0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x4c,
+  0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x54, 0x68, 0x61, 0x6e, 0x20, 0x42,
+  0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x53, 0x69, 0x7a, 0x65, 0x20, 0x4b, 0x65,
+  0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x72,
+  0x20, 0x54, 0x68, 0x61, 0x6e, 0x20, 0x4f, 0x6e, 0x65, 0x20, 0x42, 0x6c,
+  0x6f, 0x63, 0x6b, 0x2d, 0x53, 0x69, 0x7a, 0x65, 0x20, 0x44, 0x61, 0x74, 0x61
+};
+
+static const uint8_t hmac_sha1_tc_7_result_sha1[] = { /* 20 bytes */
+  0xe8, 0xe9, 0x9d, 0x0f, 0x45, 0x23, 0x7d, 0x78, 0x6d, 0x6b, 0xba, 0xa7,
+  0x96, 0x5c, 0x78, 0x08, 0xbb, 0xff, 0x1a, 0x91
+};
+
+/* HMAC-SHA-2 test cases from RFC 4231. */
+
+static const uint8_t hmac_sha2_tc_1_key[] = { /* 20 bytes */
+  0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+  0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+};
+
+/* 'Hi There' */
+static const uint8_t hmac_sha2_tc_1_data[] = { /* 8 bytes */
+  0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65
+};
+
+static const uint8_t hmac_sha2_tc_1_result_sha256[] = { /* 32 bytes */
+  0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce,
+  0xaf, 0x0b, 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7,
+  0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7
+};
+
+static const uint8_t hmac_sha2_tc_1_result_sha384[] = { /* 48 bytes */
+  0xaf, 0xd0, 0x39, 0x44, 0xd8, 0x48, 0x95, 0x62, 0x6b, 0x08, 0x25, 0xf4,
+  0xab, 0x46, 0x90, 0x7f, 0x15, 0xf9, 0xda, 0xdb, 0xe4, 0x10, 0x1e, 0xc6,
+  0x82, 0xaa, 0x03, 0x4c, 0x7c, 0xeb, 0xc5, 0x9c, 0xfa, 0xea, 0x9e, 0xa9,
+  0x07, 0x6e, 0xde, 0x7f, 0x4a, 0xf1, 0x52, 0xe8, 0xb2, 0xfa, 0x9c, 0xb6
+};
+
+static const uint8_t hmac_sha2_tc_1_result_sha512[] = { /* 64 bytes */
+  0x87, 0xaa, 0x7c, 0xde, 0xa5, 0xef, 0x61, 0x9d, 0x4f, 0xf0, 0xb4, 0x24,
+  0x1a, 0x1d, 0x6c, 0xb0, 0x23, 0x79, 0xf4, 0xe2, 0xce, 0x4e, 0xc2, 0x78,
+  0x7a, 0xd0, 0xb3, 0x05, 0x45, 0xe1, 0x7c, 0xde, 0xda, 0xa8, 0x33, 0xb7,
+  0xd6, 0xb8, 0xa7, 0x02, 0x03, 0x8b, 0x27, 0x4e, 0xae, 0xa3, 0xf4, 0xe4,
+  0xbe, 0x9d, 0x91, 0x4e, 0xeb, 0x61, 0xf1, 0x70, 0x2e, 0x69, 0x6c, 0x20,
+  0x3a, 0x12, 0x68, 0x54
+};
+
+/* 'Jefe' */
+static const uint8_t hmac_sha2_tc_2_key[] = { /* 4 bytes */
+  0x4a, 0x65, 0x66, 0x65
+};
+
+/* 'what do ya want for nothing?' */
+static const uint8_t hmac_sha2_tc_2_data[] = { /* 28 bytes */
+  0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20, 0x79, 0x61, 0x20, 0x77,
+  0x61, 0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68,
+  0x69, 0x6e, 0x67, 0x3f
+};
+
+static const uint8_t hmac_sha2_tc_2_result_sha256[] = { /* 32 bytes */
+  0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24, 0x26,
+  0x08, 0x95, 0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83,
+  0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43
+};
+
+static const uint8_t hmac_sha2_tc_2_result_sha384[] = { /* 48 bytes */
+  0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31, 0x61, 0x7f, 0x78, 0xd2,
+  0xb5, 0x8a, 0x6b, 0x1b, 0x9c, 0x7e, 0xf4, 0x64, 0xf5, 0xa0, 0x1b, 0x47,
+  0xe4, 0x2e, 0xc3, 0x73, 0x63, 0x22, 0x44, 0x5e, 0x8e, 0x22, 0x40, 0xca,
+  0x5e, 0x69, 0xe2, 0xc7, 0x8b, 0x32, 0x39, 0xec, 0xfa, 0xb2, 0x16, 0x49
+};
+
+static const uint8_t hmac_sha2_tc_2_result_sha512[] = { /* 64 bytes */
+  0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2, 0xe3, 0x95, 0xfb, 0xe7,
+  0x3b, 0x56, 0xe0, 0xa3, 0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6,
+  0x10, 0x27, 0x0c, 0xd7, 0xea, 0x25, 0x05, 0x54, 0x97, 0x58, 0xbf, 0x75,
+  0xc0, 0x5a, 0x99, 0x4a, 0x6d, 0x03, 0x4f, 0x65, 0xf8, 0xf0, 0xe6, 0xfd,
+  0xca, 0xea, 0xb1, 0xa3, 0x4d, 0x4a, 0x6b, 0x4b, 0x63, 0x6e, 0x07, 0x0a,
+  0x38, 0xbc, 0xe7, 0x37
+};
+
+static const uint8_t hmac_sha2_tc_3_key[] = { /* 20 bytes */
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+static const uint8_t hmac_sha2_tc_3_data[] = { /* 50 bytes */
+  0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+  0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+  0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+  0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+  0xdd, 0xdd
+};
+
+static const uint8_t hmac_sha2_tc_3_result_sha256[] = { /* 32 bytes */
+  0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8, 0xeb,
+  0xd0, 0x91, 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22,
+  0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe
+};
+
+static const uint8_t hmac_sha2_tc_3_result_sha384[] = { /* 48 bytes */
+  0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a, 0x0a, 0xa2, 0xac, 0xe0,
+  0x14, 0xc8, 0xa8, 0x6f, 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb,
+  0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b, 0x2a, 0x5a, 0xb3, 0x9d,
+  0xc1, 0x38, 0x14, 0xb9, 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27
+};
+
+static const uint8_t hmac_sha2_tc_3_result_sha512[] = { /* 64 bytes */
+  0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84, 0xef, 0xb0, 0xf0, 0x75,
+  0x6c, 0x89, 0x0b, 0xe9, 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36,
+  0x55, 0xf8, 0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39, 0xbf, 0x3e, 0x84, 0x82,
+  0x79, 0xa7, 0x22, 0xc8, 0x06, 0xb4, 0x85, 0xa4, 0x7e, 0x67, 0xc8, 0x07,
+  0xb9, 0x46, 0xa3, 0x37, 0xbe, 0xe8, 0x94, 0x26, 0x74, 0x27, 0x88, 0x59,
+  0xe1, 0x32, 0x92, 0xfb
+};
+
+static const uint8_t hmac_sha2_tc_4_key[] = { /* 25 bytes */
+  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
+  0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19
+};
+
+static const uint8_t hmac_sha2_tc_4_data[] = { /* 50 bytes */
+  0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+  0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+  0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+  0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+  0xcd, 0xcd
+};
+
+static const uint8_t hmac_sha2_tc_4_result_sha256[] = { /* 32 bytes */
+  0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, 0xa4, 0xcc, 0x81, 0x98,
+  0x99, 0xf2, 0x08, 0x3a, 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07,
+  0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b
+};
+
+static const uint8_t hmac_sha2_tc_4_result_sha384[] = { /* 48 bytes */
+  0x3e, 0x8a, 0x69, 0xb7, 0x78, 0x3c, 0x25, 0x85, 0x19, 0x33, 0xab, 0x62,
+  0x90, 0xaf, 0x6c, 0xa7, 0x7a, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9c,
+  0xc5, 0x57, 0x7c, 0x6e, 0x1f, 0x57, 0x3b, 0x4e, 0x68, 0x01, 0xdd, 0x23,
+  0xc4, 0xa7, 0xd6, 0x79, 0xcc, 0xf8, 0xa3, 0x86, 0xc6, 0x74, 0xcf, 0xfb
+};
+
+static const uint8_t hmac_sha2_tc_4_result_sha512[] = { /* 64 bytes */
+  0xb0, 0xba, 0x46, 0x56, 0x37, 0x45, 0x8c, 0x69, 0x90, 0xe5, 0xa8, 0xc5,
+  0xf6, 0x1d, 0x4a, 0xf7, 0xe5, 0x76, 0xd9, 0x7f, 0xf9, 0x4b, 0x87, 0x2d,
+  0xe7, 0x6f, 0x80, 0x50, 0x36, 0x1e, 0xe3, 0xdb, 0xa9, 0x1c, 0xa5, 0xc1,
+  0x1a, 0xa2, 0x5e, 0xb4, 0xd6, 0x79, 0x27, 0x5c, 0xc5, 0x78, 0x80, 0x63,
+  0xa5, 0xf1, 0x97, 0x41, 0x12, 0x0c, 0x4f, 0x2d, 0xe2, 0xad, 0xeb, 0xeb,
+  0x10, 0xa2, 0x98, 0xdd
+};
+
+/* Skipping HMAC-SHA-2 test case 5. */
+
+static const uint8_t hmac_sha2_tc_6_key[] = { /* 131 bytes */
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+/* 'Test Using Larger Than Block-Size Key - Hash Key First' */
+static const uint8_t hmac_sha2_tc_6_data[] = { /* 54 bytes */
+  0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x4c,
+  0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x54, 0x68, 0x61, 0x6e, 0x20, 0x42,
+  0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x53, 0x69, 0x7a, 0x65, 0x20, 0x4b, 0x65,
+  0x79, 0x20, 0x2d, 0x20, 0x48, 0x61, 0x73, 0x68, 0x20, 0x4b, 0x65, 0x79,
+  0x20, 0x46, 0x69, 0x72, 0x73, 0x74
+};
+
+static const uint8_t hmac_sha2_tc_6_result_sha256[] = { /* 32 bytes */
+  0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26, 0xaa,
+  0xcb, 0xf5, 0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14,
+  0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54
+};
+
+static const uint8_t hmac_sha2_tc_6_result_sha384[] = { /* 48 bytes */
+  0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90, 0x88, 0xd2, 0xc6, 0x3a,
+  0x04, 0x1b, 0xc5, 0xb4, 0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f,
+  0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6, 0x0c, 0x2e, 0xf6, 0xab,
+  0x40, 0x30, 0xfe, 0x82, 0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52
+};
+
+static const uint8_t hmac_sha2_tc_6_result_sha512[] = { /* 64 bytes */
+  0x80, 0xb2, 0x42, 0x63, 0xc7, 0xc1, 0xa3, 0xeb, 0xb7, 0x14, 0x93, 0xc1,
+  0xdd, 0x7b, 0xe8, 0xb4, 0x9b, 0x46, 0xd1, 0xf4, 0x1b, 0x4a, 0xee, 0xc1,
+  0x12, 0x1b, 0x01, 0x37, 0x83, 0xf8, 0xf3, 0x52, 0x6b, 0x56, 0xd0, 0x37,
+  0xe0, 0x5f, 0x25, 0x98, 0xbd, 0x0f, 0xd2, 0x21, 0x5d, 0x6a, 0x1e, 0x52,
+  0x95, 0xe6, 0x4f, 0x73, 0xf6, 0x3f, 0x0a, 0xec, 0x8b, 0x91, 0x5a, 0x98,
+  0x5d, 0x78, 0x65, 0x98
+};
+
+static const uint8_t hmac_sha2_tc_7_key[] = { /* 131 bytes */
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+  0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+};
+
+/* 'This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.' */
+static const uint8_t hmac_sha2_tc_7_data[] = { /* 152 bytes */
+  0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x65,
+  0x73, 0x74, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x20, 0x6c,
+  0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x62,
+  0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x6b, 0x65,
+  0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x6c, 0x61, 0x72, 0x67,
+  0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x62, 0x6c, 0x6f, 0x63,
+  0x6b, 0x2d, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x2e,
+  0x20, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x6e, 0x65, 0x65,
+  0x64, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x68, 0x61, 0x73,
+  0x68, 0x65, 0x64, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x20, 0x62,
+  0x65, 0x69, 0x6e, 0x67, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x62, 0x79,
+  0x20, 0x74, 0x68, 0x65, 0x20, 0x48, 0x4d, 0x41, 0x43, 0x20, 0x61, 0x6c,
+  0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x2e
+};
+
+static const uint8_t hmac_sha2_tc_7_result_sha256[] = { /* 32 bytes */
+  0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f, 0xbc,
+  0xd5, 0xb0, 0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93,
+  0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2
+};
+
+static const uint8_t hmac_sha2_tc_7_result_sha384[] = { /* 48 bytes */
+  0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d, 0x35, 0x1e, 0x2f, 0x25,
+  0x4e, 0x8f, 0xd3, 0x2c, 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a,
+  0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5, 0xa6, 0x78, 0xcc, 0x31,
+  0xe7, 0x99, 0x17, 0x6d, 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e
+};
+
+static const uint8_t hmac_sha2_tc_7_result_sha512[] = { /* 64 bytes */
+  0xe3, 0x7b, 0x6a, 0x77, 0x5d, 0xc8, 0x7d, 0xba, 0xa4, 0xdf, 0xa9, 0xf9,
+  0x6e, 0x5e, 0x3f, 0xfd, 0xde, 0xbd, 0x71, 0xf8, 0x86, 0x72, 0x89, 0x86,
+  0x5d, 0xf5, 0xa3, 0x2d, 0x20, 0xcd, 0xc9, 0x44, 0xb6, 0x02, 0x2c, 0xac,
+  0x3c, 0x49, 0x82, 0xb1, 0x0d, 0x5e, 0xeb, 0x55, 0xc3, 0xe4, 0xde, 0x15,
+  0x13, 0x46, 0x76, 0xfb, 0x6d, 0xe0, 0x44, 0x60, 0x65, 0xc9, 0x74, 0x40,
+  0xfa, 0x8c, 0x6a, 0x58
+};
+
+static int _test_hash(const hal_digest_algorithm_t alg,
+                      const uint8_t * const data, const size_t data_len,
+                      const uint8_t * const result, const size_t result_len,
+                      const char * const label)
+{
+  uint8_t digest[512];
+  hal_error_t err;
+
+  assert(data != NULL && result != NULL && label != NULL);
+  assert(result_len <= sizeof(digest));
+
+  printf("Starting %s test\n", label);
+
+  hal_client_handle_t client = {0};
+  hal_session_handle_t session = {0};
+  hal_hash_handle_t hash;
+
+  if ((err = hal_rpc_hash_initialize(client, session, &hash, alg, NULL, 0)) != HAL_OK) {
+    printf("Failed while initializing hash: %s\n", hal_error_string(err));
+    return 0;
+  }
+
+  if ((err = hal_rpc_hash_update(hash, data, data_len)) != HAL_OK) {
+    printf("Failed while updating hash: %s\n", hal_error_string(err));
+    return 0;
+  }
+
+  if ((err = hal_rpc_hash_finalize(hash, digest, sizeof(digest))) != HAL_OK) {
+    printf("Failed while finalizing hash: %s\n", hal_error_string(err));
+    return 0;
+  }
+
+  printf("Comparing result with known value\n");
+  if (memcmp(result, digest, result_len)) {
+    size_t i;
+    printf("MISMATCH\nExpected:");
+    for (i = 0; i < result_len; i++)
+      printf(" %02x", result[i]);
+    printf("\nGot:     ");
+    for (i = 0; i < result_len; i++)
+      printf(" %02x", digest[i]);
+    printf("\n");
+    return 0;
+  }
+
+  printf("OK\n");
+    return 1;
+}
+
+static int _test_hmac(const hal_digest_algorithm_t alg,
+                      const uint8_t * const key,  const size_t key_len,
+                      const uint8_t * const data, const size_t data_len,
+                      const uint8_t * const result, const size_t result_len,
+                      const char * const label)
+{
+  uint8_t digest[512];
+  hal_error_t err;
+
+  assert(data != NULL && result != NULL && label != NULL);
+  assert(result_len <= sizeof(digest));
+
+  printf("Starting %s test\n", label);
+
+  hal_client_handle_t client = {0};
+  hal_session_handle_t session = {0};
+  hal_hash_handle_t hash;
+
+  if ((err = hal_rpc_hash_initialize(client, session, &hash, alg, key, key_len)) != HAL_OK) {
+    printf("Failed while initializing HMAC: %s\n", hal_error_string(err));
+    return 0;
+  }
+
+  if ((err = hal_rpc_hash_update(hash, data, data_len)) != HAL_OK) {
+    printf("Failed while updating HMAC: %s\n", hal_error_string(err));
+    return 0;
+  }
+
+  if ((err = hal_rpc_hash_finalize(hash, digest, sizeof(digest))) != HAL_OK) {
+    printf("Failed while finalizing HMAC: %s\n", hal_error_string(err));
+    return 0;
+  }
+
+  printf("Comparing result with known value\n");
+  if (memcmp(result, digest, result_len)) {
+    size_t i;
+    printf("MISMATCH\nExpected:");
+    for (i = 0; i < result_len; i++)
+      printf(" %02x", result[i]);
+    printf("\nGot:     ");
+    for (i = 0; i < result_len; i++)
+      printf(" %02x", digest[i]);
+    printf("\n");
+    return 0;
+  }
+
+  printf("OK\n");
+    return 1;
+}
+
+#define test_hash(_alg_, _data_, _result_, _label_) \
+  _test_hash(_alg_, _data_, sizeof(_data_), _result_, sizeof(_result_), _label_)
+
+#define test_hmac(_alg_, _key_, _data_, _result_, _label_) \
+  _test_hmac(_alg_, _key_, sizeof(_key_), _data_, sizeof(_data_), _result_, sizeof(_result_), _label_)
+
+int main (int argc, char *argv[])
+{
+//  rpc_client_init(RPC_LOCAL);
+  rpc_client_init(RPC_REMOTE);
+
+  int ok = 1;
+
+  ok &= test_hash(hal_digest_algorithm_sha1,   nist_512_single, sha1_single_digest, "SHA-1 single block");
+  ok &= test_hash(hal_digest_algorithm_sha1,   nist_512_double, sha1_double_digest, "SHA-1 double block");
+
+  ok &= test_hash(hal_digest_algorithm_sha256, nist_512_single, sha256_single_digest, "SHA-256 single block");
+  ok &= test_hash(hal_digest_algorithm_sha256, nist_512_double, sha256_double_digest, "SHA-256 double block");
+
+  ok &= test_hash(hal_digest_algorithm_sha512_224, nist_1024_single, sha512_224_single_digest, "SHA-512/224 single block");
+  ok &= test_hash(hal_digest_algorithm_sha512_224, nist_1024_double, sha512_224_double_digest, "SHA-512/224 double block");
+
+  ok &= test_hash(hal_digest_algorithm_sha512_256, nist_1024_single, sha512_256_single_digest, "SHA-512/256 single block");
+  ok &= test_hash(hal_digest_algorithm_sha512_256, nist_1024_double, sha512_256_double_digest, "SHA-512/256 double block");
+
+  ok &= test_hash(hal_digest_algorithm_sha384, nist_1024_single, sha384_single_digest, "SHA-384 single block");
+  ok &= test_hash(hal_digest_algorithm_sha384, nist_1024_double, sha384_double_digest, "SHA-384 double block");
+
+  ok &= test_hash(hal_digest_algorithm_sha512, nist_1024_single, sha512_single_digest, "SHA-512 single block");
+  ok &= test_hash(hal_digest_algorithm_sha512, nist_1024_double, sha512_double_digest, "SHA-512 double block");
+
+  ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_1_key, hmac_sha1_tc_1_data, hmac_sha1_tc_1_result_sha1, "HMAC-SHA-1 test case 1");
+  ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_2_key, hmac_sha1_tc_2_data, hmac_sha1_tc_2_result_sha1, "HMAC-SHA-1 test case 2");
+  ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_3_key, hmac_sha1_tc_3_data, hmac_sha1_tc_3_result_sha1, "HMAC-SHA-1 test case 3");
+  ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_4_key, hmac_sha1_tc_4_data, hmac_sha1_tc_4_result_sha1, "HMAC-SHA-1 test case 4");
+  ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_5_key, hmac_sha1_tc_5_data, hmac_sha1_tc_5_result_sha1, "HMAC-SHA-1 test case 5");
+  ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_6_key, hmac_sha1_tc_6_data, hmac_sha1_tc_6_result_sha1, "HMAC-SHA-1 test case 6");
+  ok &= test_hmac(hal_digest_algorithm_sha1, hmac_sha1_tc_7_key, hmac_sha1_tc_7_data, hmac_sha1_tc_7_result_sha1, "HMAC-SHA-1 test case 7");
+
+  ok &= test_hmac(hal_digest_algorithm_sha256, hmac_sha2_tc_1_key, hmac_sha2_tc_1_data, hmac_sha2_tc_1_result_sha256, "HMAC-SHA-256 test case 1");
+  ok &= test_hmac(hal_digest_algorithm_sha256, hmac_sha2_tc_2_key, hmac_sha2_tc_2_data, hmac_sha2_tc_2_result_sha256, "HMAC-SHA-256 test case 2");
+  ok &= test_hmac(hal_digest_algorithm_sha256, hmac_sha2_tc_3_key, hmac_sha2_tc_3_data, hmac_sha2_tc_3_result_sha256, "HMAC-SHA-256 test case 3");
+  ok &= test_hmac(hal_digest_algorithm_sha256, hmac_sha2_tc_4_key, hmac_sha2_tc_4_data, hmac_sha2_tc_4_result_sha256, "HMAC-SHA-256 test case 4");
+  ok &= test_hmac(hal_digest_algorithm_sha256, hmac_sha2_tc_6_key, hmac_sha2_tc_6_data, hmac_sha2_tc_6_result_sha256, "HMAC-SHA-256 test case 6");
+  ok &= test_hmac(hal_digest_algorithm_sha256, hmac_sha2_tc_7_key, hmac_sha2_tc_7_data, hmac_sha2_tc_7_result_sha256, "HMAC-SHA-256 test case 7");
+
+  ok &= test_hmac(hal_digest_algorithm_sha384, hmac_sha2_tc_1_key, hmac_sha2_tc_1_data, hmac_sha2_tc_1_result_sha384, "HMAC-SHA-384 test case 1");
+  ok &= test_hmac(hal_digest_algorithm_sha384, hmac_sha2_tc_2_key, hmac_sha2_tc_2_data, hmac_sha2_tc_2_result_sha384, "HMAC-SHA-384 test case 2");
+  ok &= test_hmac(hal_digest_algorithm_sha384, hmac_sha2_tc_3_key, hmac_sha2_tc_3_data, hmac_sha2_tc_3_result_sha384, "HMAC-SHA-384 test case 3");
+  ok &= test_hmac(hal_digest_algorithm_sha384, hmac_sha2_tc_4_key, hmac_sha2_tc_4_data, hmac_sha2_tc_4_result_sha384, "HMAC-SHA-384 test case 4");
+  ok &= test_hmac(hal_digest_algorithm_sha384, hmac_sha2_tc_6_key, hmac_sha2_tc_6_data, hmac_sha2_tc_6_result_sha384, "HMAC-SHA-384 test case 6");
+  ok &= test_hmac(hal_digest_algorithm_sha384, hmac_sha2_tc_7_key, hmac_sha2_tc_7_data, hmac_sha2_tc_7_result_sha384, "HMAC-SHA-384 test case 7");
+
+  ok &= test_hmac(hal_digest_algorithm_sha512, hmac_sha2_tc_1_key, hmac_sha2_tc_1_data, hmac_sha2_tc_1_result_sha512, "HMAC-SHA-512 test case 1");
+  ok &= test_hmac(hal_digest_algorithm_sha512, hmac_sha2_tc_2_key, hmac_sha2_tc_2_data, hmac_sha2_tc_2_result_sha512, "HMAC-SHA-512 test case 2");
+  ok &= test_hmac(hal_digest_algorithm_sha512, hmac_sha2_tc_3_key, hmac_sha2_tc_3_data, hmac_sha2_tc_3_result_sha512, "HMAC-SHA-512 test case 3");
+  ok &= test_hmac(hal_digest_algorithm_sha512, hmac_sha2_tc_4_key, hmac_sha2_tc_4_data, hmac_sha2_tc_4_result_sha512, "HMAC-SHA-512 test case 4");
+  ok &= test_hmac(hal_digest_algorithm_sha512, hmac_sha2_tc_6_key, hmac_sha2_tc_6_data, hmac_sha2_tc_6_result_sha512, "HMAC-SHA-512 test case 6");
+  ok &= test_hmac(hal_digest_algorithm_sha512, hmac_sha2_tc_7_key, hmac_sha2_tc_7_data, hmac_sha2_tc_7_result_sha512, "HMAC-SHA-512 test case 7");
+
+  return !ok;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tests/test-rpc_server.c b/tests/test-rpc_server.c
new file mode 100644
index 0000000..6eff755
--- /dev/null
+++ b/tests/test-rpc_server.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include <hal.h>
+#include <hal_internal.h>
+
+int main (int argc, char *argv[])
+{
+    if (rpc_server_init() != HAL_OK)
+	return 1;
+
+    rpc_server_main();
+    return 0;
+}

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the Commits mailing list