[Cryptech-Commits] [sw/libhal] 01/03: Start cleaning up PIN code.

git at cryptech.is git at cryptech.is
Thu May 26 04:11:07 UTC 2016


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

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

commit 083d01731ffebb348c749ad6ccdb0256571835c7
Author: Rob Austein <sra at hactrn.net>
AuthorDate: Wed May 25 21:40:18 2016 -0400

    Start cleaning up PIN code.
---
 GNUmakefile                 | 10 ++++--
 hal.h                       |  3 ++
 ks.c                        | 23 ++++++++++++
 last_gasp_pin_internal.h    |  9 +++++
 rpc_api.c                   | 31 +++++++++++++---
 rpc_misc.c                  | 68 +++++++++++++++++------------------
 rpc_pkey.c                  | 14 ++++++++
 utils/last_gasp_default_pin | 86 +++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 203 insertions(+), 41 deletions(-)

diff --git a/GNUmakefile b/GNUmakefile
index 3dcc9da..b74a63f 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -189,10 +189,14 @@ ${OBJ}: ${INC}
 ${LIB}: ${OBJ}
 	${AR} rcs $@ $^
 
-asn1.o rsa.o ecdsa.o:		asn1_internal.h
-ecdsa.o:			ecdsa_curves.h
-novena-eim.o hal_io_eim.o:	novena-eim.h
+asn1.o rsa.o ecdsa.o:				asn1_internal.h
+ecdsa.o:					ecdsa_curves.h
+novena-eim.o hal_io_eim.o:			novena-eim.h
 slip.o rpc_client_serial.o rpc_server_serial.o:	slip_internal.h
+ks.o:						last_gasp_pin_internal.h
+
+last_gasp_pin_internal.h:
+	./utils/last_gasp_default_pin >$@
 
 test: all
 	export RPC_CLIENT RPC_SERVER
diff --git a/hal.h b/hal.h
index 5021020..f62c89e 100644
--- a/hal.h
+++ b/hal.h
@@ -568,6 +568,9 @@ typedef struct { uint32_t handle; } hal_session_handle_t;
 
 typedef enum { HAL_USER_NONE, HAL_USER_NORMAL, HAL_USER_SO, HAL_USER_WHEEL } hal_user_t;
 
+extern const size_t hal_rpc_min_pin_length;
+extern const size_t hal_rpc_max_pin_length;
+
 extern hal_error_t hal_rpc_set_pin(const hal_client_handle_t client,
                                    const hal_user_t user,
                                    const char * const newpin, const size_t newpin_len);
diff --git a/ks.c b/ks.c
index 80cbda4..b6cb32f 100644
--- a/ks.c
+++ b/ks.c
@@ -38,6 +38,7 @@
 
 #include "hal.h"
 #include "hal_internal.h"
+#include "last_gasp_pin_internal.h"
 
 #define KEK_LENGTH (bitsToBytes(256))
 
@@ -327,6 +328,28 @@ hal_error_t hal_ks_get_pin(const hal_user_t user,
   default:		return HAL_ERROR_BAD_ARGUMENTS;
   }
 
+  /*
+   * If we were looking for the WHEEL PIN and it appears to be
+   * completely unset, return the compiled-in last-gasp PIN.  This is
+   * not a great answer, but we need some kind of bootstrapping
+   * mechanism.  Feel free to suggest something better.
+   *
+   * We probably need some more general "have we been initialized?"
+   * state somewhere, and might want to refuse to do things like
+   * storing keys until we've been initialized and the appropriate
+   * PINs have been set.
+   */
+
+  if (user == HAL_USER_WHEEL && (*pin)->iterations == 0) {
+    uint8_t u = 0;
+    for (int i = 0; i < sizeof((*pin)->pin); i++)
+      u |= (*pin)->pin[i];
+    for (int i = 0; i < sizeof((*pin)->salt); i++)
+      u |= (*pin)->salt[i];
+    if (u == 0)
+      *pin = &hal_last_gasp_pin;
+  }
+
   return HAL_OK;
 }
 
diff --git a/last_gasp_pin_internal.h b/last_gasp_pin_internal.h
new file mode 100644
index 0000000..13c3078
--- /dev/null
+++ b/last_gasp_pin_internal.h
@@ -0,0 +1,9 @@
+/*
+ * Automatically generated by a script, do not edit.
+ */
+
+static const hal_ks_pin_t hal_last_gasp_pin = {
+  100000,
+  {0xc1, 0xdb, 0xbf, 0x89, 0x5b, 0xd4, 0xa5, 0x64, 0xfb, 0xbc, 0x33, 0xcb, 0xf8, 0x5a, 0xb0, 0xfa, 0xa8, 0x13, 0xd8, 0x9e, 0x28, 0xdf, 0x28, 0x15, 0x21, 0x0d, 0x7e, 0x9d, 0x53, 0xd9, 0xfc, 0x32},
+  {0xb3, 0xbf, 0x4d, 0xcd, 0xa2, 0x1a, 0x96, 0x63, 0x2b, 0xc4, 0x0c, 0xdb, 0xa1, 0x5d, 0x34, 0xfa}
+};
diff --git a/rpc_api.c b/rpc_api.c
index 5bab506..a19bdb4 100644
--- a/rpc_api.c
+++ b/rpc_api.c
@@ -38,6 +38,25 @@
 
 const hal_hash_handle_t hal_hash_handle_none = {HAL_HANDLE_NONE};
 
+/*
+ * PIN lengths.  These are somewhat arbitrary, and the current values
+ * are really placeholders until we figure out something better.
+ * Minimum length here is almost certainly too short for production
+ * use, we allow it because most test programs fail if we insist on a
+ * PIN long enough to have any real security.
+ */
+
+#ifndef HAL_PIN_MINIMUM_LENGTH
+#define HAL_PIN_MINIMUM_LENGTH          4
+#endif
+
+#ifndef HAL_PIN_MAXIMUM_LENGTH
+#define HAL_PIN_MAXIMUM_LENGTH          4096
+#endif
+
+const size_t hal_rpc_min_pin_length = HAL_PIN_MINIMUM_LENGTH;
+const size_t hal_rpc_max_pin_length = HAL_PIN_MAXIMUM_LENGTH;
+
 static inline int check_pkey_type(const hal_key_type_t type)
 {
   switch (type) {
@@ -103,13 +122,14 @@ hal_error_t hal_rpc_get_random(void *buffer, const size_t length)
   return hal_rpc_misc_dispatch->get_random(buffer, length);
 }
 
-#warning Perhaps we should be enforcing a minimum PIN length here
-
 hal_error_t hal_rpc_set_pin(const hal_client_handle_t client,
                             const hal_user_t user,
                             const char * const newpin, const size_t newpin_len)
 {
-  if (newpin == NULL || newpin_len == 0 || (user != HAL_USER_NORMAL && user != HAL_USER_SO && user != HAL_USER_WHEEL))
+  if (newpin == NULL ||
+      newpin_len < hal_rpc_min_pin_length ||
+      newpin_len > hal_rpc_max_pin_length ||
+      (user != HAL_USER_NORMAL && user != HAL_USER_SO && user != HAL_USER_WHEEL))
     return HAL_ERROR_BAD_ARGUMENTS;
   return hal_rpc_misc_dispatch->set_pin(client, user, newpin, newpin_len);
 }
@@ -118,7 +138,10 @@ hal_error_t hal_rpc_login(const hal_client_handle_t client,
                           const hal_user_t user,
                           const char * const pin, const size_t pin_len)
 {
-  if (pin == NULL || pin_len == 0 || (user != HAL_USER_NORMAL && user != HAL_USER_SO && user != HAL_USER_WHEEL))
+  if (pin == NULL ||
+      pin_len < hal_rpc_min_pin_length ||
+      pin_len > hal_rpc_max_pin_length ||
+      (user != HAL_USER_NORMAL && user != HAL_USER_SO && user != HAL_USER_WHEEL))
     return HAL_ERROR_BAD_ARGUMENTS;
   return hal_rpc_misc_dispatch->login(client, user, pin, pin_len);
 }
diff --git a/rpc_misc.c b/rpc_misc.c
index 9b5ed1b..18f4083 100644
--- a/rpc_misc.c
+++ b/rpc_misc.c
@@ -72,8 +72,6 @@ static hal_error_t get_random(void *buffer, const size_t length)
  * PIN to be changed a second time without toasting the keystore.
  */
 
-#warning PIN code not yet fully implemented
-
 typedef struct {
   hal_client_handle_t handle;
   hal_user_t logged_in;
@@ -123,37 +121,6 @@ static inline client_slot_t *find_handle(const hal_client_handle_t handle)
   return NULL;
 }
 
-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)
-{
-  assert(newpin != NULL && newpin_len != 0);
-
-#warning Need access control to decide who is allowed to set this PIN
-#warning Need length checks (here or in caller) on supplied PIN
-
-  const hal_ks_pin_t *pp;
-  hal_error_t err;
-
-  if ((err = hal_ks_get_pin(user, &pp)) != HAL_OK)
-    return err;
-
-  hal_ks_pin_t p = *pp;
-
-  if (p.iterations == 0)
-    p.iterations = HAL_PIN_DEFAULT_ITERATIONS;
-
-  if ((err = hal_get_random(NULL, p.salt, sizeof(p.salt)))      != HAL_OK ||
-      (err = hal_pbkdf2(NULL, hal_hash_sha256,
-                        (const uint8_t *) newpin, newpin_len,
-                        p.salt, sizeof(p.salt),
-                        p.pin,  sizeof(p.pin), p.iterations))   != HAL_OK ||
-      (err = hal_ks_set_pin(user, &p))                          != HAL_OK)
-    return err;
-
-  return HAL_OK;
-}
-
 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)
@@ -168,9 +135,10 @@ static hal_error_t login(const hal_client_handle_t client,
     return err;
 
   uint8_t buf[sizeof(p->pin)];
+  const uint32_t iterations = p->iterations == 0 ? HAL_PIN_DEFAULT_ITERATIONS : p->iterations;
 
   if ((err = hal_pbkdf2(NULL, hal_hash_sha256, (const uint8_t *) pin, pin_len,
-                        p->salt, sizeof(p->salt), buf, sizeof(buf), p->iterations)) != HAL_OK)
+                        p->salt, sizeof(p->salt), buf, sizeof(buf), iterations)) != HAL_OK)
     return err;
 
   unsigned diff = 0;
@@ -224,6 +192,38 @@ static hal_error_t logout_all(void)
   return HAL_OK;
 }
 
+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)
+{
+  assert(newpin != NULL && newpin_len >= hal_rpc_min_pin_length && newpin_len <= hal_rpc_max_pin_length);
+
+  if ((user != HAL_USER_NORMAL || is_logged_in(client, HAL_USER_SO) != HAL_OK) &&
+      is_logged_in(client, HAL_USER_WHEEL) != HAL_OK)
+    return HAL_ERROR_FORBIDDEN;
+
+  const hal_ks_pin_t *pp;
+  hal_error_t err;
+
+  if ((err = hal_ks_get_pin(user, &pp)) != HAL_OK)
+    return err;
+
+  hal_ks_pin_t p = *pp;
+
+  if (p.iterations == 0)
+    p.iterations = HAL_PIN_DEFAULT_ITERATIONS;
+
+  if ((err = hal_get_random(NULL, p.salt, sizeof(p.salt)))      != HAL_OK ||
+      (err = hal_pbkdf2(NULL, hal_hash_sha256,
+                        (const uint8_t *) newpin, newpin_len,
+                        p.salt, sizeof(p.salt),
+                        p.pin,  sizeof(p.pin), p.iterations))   != HAL_OK ||
+      (err = hal_ks_set_pin(user, &p))                          != HAL_OK)
+    return err;
+
+  return HAL_OK;
+}
+
 const hal_rpc_misc_dispatch_t hal_rpc_local_misc_dispatch = {
   set_pin,
   login,
diff --git a/rpc_pkey.c b/rpc_pkey.c
index 0cf1a88..6b548d5 100644
--- a/rpc_pkey.c
+++ b/rpc_pkey.c
@@ -123,6 +123,20 @@ static inline pkey_slot_t *find_handle(const hal_pkey_handle_t handle)
   return NULL;
 }
 
+#warning Still need access control on pkey objects based on current login state
+/*
+ * This would be simple, except for PKCS #11 non-token objects (CKA_TOKEN = CK_FALSE).
+ * Need to check detailed PKCS #11 rules, but, from memory, we may be supposed to allow
+ * access to non-token objects even when not logged in.  Maybe.  Rules are complex.
+ *
+ * I think the libhal translation of this resolves around what we've
+ * been calling the PROXIMATE flags (which probably ought to be
+ * renamed to *_NONTOKEN_*, slightly less confusing name).  For token
+ * objects, we insist on being logged in properly; for non-token
+ * objects, we do whatever silly thing PKCS #11 wants us to do,
+ * probably defaulting to requiring login if PKCS #11 gives us a choice.
+ */
+
 /*
  * Construct a PKCS #1 DigestInfo object.  This requires some (very
  * basic) ASN.1 encoding, which we perform inline.
diff --git a/utils/last_gasp_default_pin b/utils/last_gasp_default_pin
new file mode 100755
index 0000000..e0d9839
--- /dev/null
+++ b/utils/last_gasp_default_pin
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+
+"""
+Somewhere, the HSM has to have a last-gasp default PIN, even if it's
+only the null string, because there has to be **some** way to
+initialize the poor thing.  Absent a better plan (feel free to
+suggest one!), this last-gasp default is compiled in.
+
+The normal value of this last-gasp PIN is deliberately chosen to be
+annoying, so that people will change it, but since the derevation
+requires running PBKDF2 and you might want a different default if
+you're compiling this for yourself, we provide the script that
+generates the default.
+"""
+
+# Author: Rob Austein
+# 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.
+
+from argparse                   import ArgumentParser, ArgumentDefaultsHelpFormatter
+from os                         import urandom
+from Crypto.Protocol.KDF        import PBKDF2
+from Crypto.Hash                import SHA256, HMAC
+
+parser = ArgumentParser(description = __doc__, formatter_class = ArgumentDefaultsHelpFormatter)
+parser.add_argument("-p", "--pin",
+                    default = "YouReallyNeedToChangeThisPINRightNowWeAreNotKidding",
+                    help    = "PIN plaintext before PBKDF2 processing")
+parser.add_argument("-i", "--iterations",
+                    type    = int,
+                    default = 100000,
+                    help    = "PBKDF2 iteration count")
+args = parser.parse_args()
+
+def HMAC_SHA256(pin, salt):
+    return HMAC.new(pin, salt, SHA256).digest()
+
+def hexify(value):
+    return ", ".join("0x%02x" % ord(v) for v in value)
+
+salt = urandom(16)
+
+pin  = PBKDF2(password = args.pin,
+              salt     = salt,
+              dkLen    = 32,
+              count    = args.iterations,
+              prf      = HMAC_SHA256)
+
+print '''\
+/*
+ * Automatically generated by a script, do not edit.
+ */
+
+static const hal_ks_pin_t hal_last_gasp_pin = {{
+  {iterations},
+  {{{pin}}},
+  {{{salt}}}
+}};'''.format(iterations = args.iterations,
+              pin        = hexify(pin),
+              salt       = hexify(salt))



More information about the Commits mailing list