aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2016-05-25 21:40:18 -0400
committerRob Austein <sra@hactrn.net>2016-05-25 21:40:18 -0400
commit083d01731ffebb348c749ad6ccdb0256571835c7 (patch)
treef883802139fa0938cfcb6784201dad80fd9e64fa
parentce4d1c7a8103be09bcee64b055408a901d068c34 (diff)
Start cleaning up PIN code.
-rw-r--r--GNUmakefile10
-rw-r--r--hal.h3
-rw-r--r--ks.c23
-rw-r--r--last_gasp_pin_internal.h9
-rw-r--r--rpc_api.c31
-rw-r--r--rpc_misc.c68
-rw-r--r--rpc_pkey.c14
-rwxr-xr-xutils/last_gasp_default_pin86
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))