summaryrefslogtreecommitdiff
path: root/lib/libutil
diff options
context:
space:
mode:
authorTed Unangst <tedu@cvs.openbsd.org>2013-06-03 21:07:03 +0000
committerTed Unangst <tedu@cvs.openbsd.org>2013-06-03 21:07:03 +0000
commit9256b485f2a4460ce3f38000bbe267afe7e42b0a (patch)
treec3cbe162c2447f55d8889e1d831418fbc0b85eee /lib/libutil
parentfae775ccad37e7f4d747f89052e7b438910ca957 (diff)
Add bcrypt_pbkdf, a password based key derivation function using bcrypt.
Technically, it's a slight variant of bcrypt better suited for use as a pluggable hash with PKCS #5 PBKDF2. ok djm (also tweak pkcs5_pbkdf2() prototype to have consistent types.)
Diffstat (limited to 'lib/libutil')
-rw-r--r--lib/libutil/Makefile8
-rw-r--r--lib/libutil/bcrypt_pbkdf.368
-rw-r--r--lib/libutil/bcrypt_pbkdf.c163
-rw-r--r--lib/libutil/pkcs5_pbkdf2.37
-rw-r--r--lib/libutil/pkcs5_pbkdf2.c10
-rw-r--r--lib/libutil/shlib_version2
-rw-r--r--lib/libutil/util.h8
7 files changed, 251 insertions, 15 deletions
diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile
index 080671083f7..d4e748231ba 100644
--- a/lib/libutil/Makefile
+++ b/lib/libutil/Makefile
@@ -1,15 +1,17 @@
-# $OpenBSD: Makefile,v 1.35 2012/09/06 19:41:59 tedu Exp $
+# $OpenBSD: Makefile,v 1.36 2013/06/03 21:07:02 tedu Exp $
# $NetBSD: Makefile,v 1.8 1996/05/16 07:03:28 thorpej Exp $
LIB= util
HDRS= util.h imsg.h
-SRCS= check_expire.c duid.c getmaxpartitions.c getrawpartition.c login.c \
+SRCS= bcrypt_pbkdf.c check_expire.c duid.c getmaxpartitions.c \
+ getrawpartition.c login.c \
login_tty.c logout.c logwtmp.c opendev.c passwd.c pty.c readlabel.c \
login_fbtab.c uucplock.c fparseln.c opendisk.c pidfile.c \
fmt_scaled.c imsg.c imsg-buffer.c pkcs5_pbkdf2.c
-MAN= check_expire.3 getmaxpartitions.3 getrawpartition.3 isduid.3 login.3 \
+MAN= bcrypt_pbkdf.3 check_expire.3 getmaxpartitions.3 getrawpartition.3 \
+ isduid.3 login.3 \
opendev.3 openpty.3 pw_init.3 pw_lock.3 readlabelfs.3 uucplock.3 \
fparseln.3 opendisk.3 login_fbtab.3 pidfile.3 fmt_scaled.3 imsg_init.3 \
pkcs5_pbkdf2.3
diff --git a/lib/libutil/bcrypt_pbkdf.3 b/lib/libutil/bcrypt_pbkdf.3
new file mode 100644
index 00000000000..65bec948ee8
--- /dev/null
+++ b/lib/libutil/bcrypt_pbkdf.3
@@ -0,0 +1,68 @@
+.\" $OpenBSD: bcrypt_pbkdf.3,v 1.1 2013/06/03 21:07:02 tedu Exp $
+.\"
+.\" Copyright (c) 2012 Ted Unangst <tedu@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: June 3 2013 $
+.Dt BCRYPT_PBKDF 3
+.Os
+.Sh NAME
+.Nm bcrypt_pbkdf
+.Nd bcrypt password-based key derivation function
+.Sh SYNOPSIS
+.Fd #include <util.h>
+.Ft int
+.Fn bcrypt_pbkdf "const char *pass" "size_t pass_len" "const uint8_t *salt" \
+ "size_t salt_len" "uint8_t *key" "size_t key_len" "unsigned int rounds"
+.Sh DESCRIPTION
+The
+.Nm
+function converts a password into a byte array suitable for use as
+an encryption key.
+The password and salt values are combined and repeatedly hashed
+.Ar rounds
+times.
+The salt value should be randomly generated beforehand.
+The repeated hashing is designed to thwart discovery of the key via
+password guessing attacks.
+The higher the number of rounds, the slower each attempt will be.
+.\" A minimum value of at least 1000 is recommended.
+.Sh RETURN VALUES
+The
+.Fn bcrypt_pbkdf
+function returns 0 to indicate success and -1 for failure.
+.\" .Sh EXAMPLES
+.\" .Sh ERRORS
+.Sh SEE ALSO
+.Xr sha1 1 ,
+.Xr bcrypt 3
+.Sh STANDARDS
+.Rs
+.%A Niels Provos and David Mazieres
+.%D June 1999
+.%T A Future-Adaptable Password Scheme
+.Re
+.Pp
+.Rs
+.%A B. Kaliski
+.%D September 2000
+.%R RFC 2898
+.%T PKCS #5: Password-Based Cryptography Specification Version 2.0
+.Re
+.\" .Sh HISTORY
+.\" .Sh AUTHORS
+.Sh CAVEATS
+This implementation deviates slightly from the PBKDF2 standard by mixing
+output key bits nonlinearly.
+.\" .Sh BUGS
diff --git a/lib/libutil/bcrypt_pbkdf.c b/lib/libutil/bcrypt_pbkdf.c
new file mode 100644
index 00000000000..732499be0c9
--- /dev/null
+++ b/lib/libutil/bcrypt_pbkdf.c
@@ -0,0 +1,163 @@
+/* $OpenBSD: bcrypt_pbkdf.c,v 1.1 2013/06/03 21:07:02 tedu Exp $ */
+/*
+ * Copyright (c) 2013 Ted Unangst <tedu@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <stdlib.h>
+#include <blf.h>
+#include <sha2.h>
+#include <string.h>
+#include <util.h>
+
+/*
+ * pkcs #5 pbkdf2 implementation using the "bcrypt" hash
+ *
+ * The bcrypt hash function is derived from the bcrypt password hashing
+ * function with the following modifications:
+ * 1. The input password and salt are preprocessed with SHA512.
+ * 2. The output length is expanded to 256 bits.
+ * 3. Subsequently the magic string to be encrypted is lengthened and modifed
+ * to "OxychromaticBlowfishSwatDynamite"
+ * 4. The hash function is defined to perform 64 rounds of initial state
+ * expansion. (More rounds are performed by iterating the hash.)
+ *
+ * Note that this implementation pulls the SHA512 operations into the caller
+ * as a performance optimization.
+ *
+ * One modification from official pbkdf2. Instead of outputting key material
+ * linearly, we mix it. pbkdf2 has a known weakness where if one uses it to
+ * generate (i.e.) 512 bits of key material for use as two 256 bit keys, an
+ * attacker can merely run once through the outer loop below, but the user
+ * always runs it twice. Shuffling output bytes requires computing the
+ * entirety of the key material to assemble any subkey. This is something a
+ * wise caller could do; we just do it for you.
+ */
+
+#define BCRYPT_BLOCKS 8
+#define BCRYPT_HASHSIZE (BCRYPT_BLOCKS * 4)
+
+static void
+bcrypt_hash(uint8_t sha2pass[SHA512_DIGEST_LENGTH],
+ uint8_t sha2salt[SHA512_DIGEST_LENGTH], uint8_t out[BCRYPT_HASHSIZE])
+{
+ blf_ctx state;
+ uint8_t ciphertext[BCRYPT_HASHSIZE] =
+ "OxychromaticBlowfishSwatDynamite";
+ uint32_t cdata[BCRYPT_BLOCKS];
+ int i;
+ uint16_t j;
+
+ /* key expansion */
+ Blowfish_initstate(&state);
+ Blowfish_expandstate(&state, sha2salt, sizeof(sha2salt), sha2pass,
+ sizeof(sha2pass));
+ for (i = 0; i < 64; i++) {
+ Blowfish_expand0state(&state, sha2salt, sizeof(sha2salt));
+ Blowfish_expand0state(&state, sha2pass, sizeof(sha2pass));
+ }
+
+ /* encryption */
+ j = 0;
+ for (i = 0; i < BCRYPT_BLOCKS; i++)
+ cdata[i] = Blowfish_stream2word(ciphertext, sizeof(ciphertext),
+ &j);
+ for (i = 0; i < 64; i++)
+ blf_enc(&state, cdata, sizeof(cdata) / sizeof(uint64_t));
+
+ /* copy out */
+ for (i = 0; i < BCRYPT_BLOCKS; i++) {
+ out[4 * i + 3] = (cdata[i] >> 24) & 0xff;
+ out[4 * i + 2] = (cdata[i] >> 16) & 0xff;
+ out[4 * i + 1] = (cdata[i] >> 8) & 0xff;
+ out[4 * i + 0] = cdata[i] & 0xff;
+ }
+
+ /* zap */
+ memset(ciphertext, 0, sizeof(ciphertext));
+ memset(cdata, 0, sizeof(cdata));
+ memset(&state, 0, sizeof(state));
+}
+
+int
+bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltlen,
+ uint8_t *key, size_t keylen, unsigned int rounds)
+{
+ SHA2_CTX ctx;
+ uint8_t sha2pass[SHA512_DIGEST_LENGTH];
+ uint8_t sha2salt[SHA512_DIGEST_LENGTH];
+ uint8_t out[BCRYPT_HASHSIZE];
+ uint8_t tmpout[BCRYPT_HASHSIZE];
+ uint8_t countsalt[4];
+ size_t i, j, amt, stride;
+ uint32_t count;
+
+ /* nothing crazy */
+ if (rounds < 1)
+ return -1;
+ if (passlen == 0 || saltlen == 0 || keylen == 0 ||
+ keylen > sizeof(out) * sizeof(out))
+ return -1;
+ stride = (keylen + sizeof(out) - 1) / sizeof(out);
+ amt = (keylen + stride - 1) / stride;
+
+ /* collapse password */
+ SHA512Init(&ctx);
+ SHA512Update(&ctx, pass, passlen);
+ SHA512Final(sha2pass, &ctx);
+
+
+ /* generate key, sizeof(out) at a time */
+ for (count = 1; keylen > 0; count++) {
+ countsalt[0] = (count >> 24) & 0xff;
+ countsalt[1] = (count >> 16) & 0xff;
+ countsalt[2] = (count >> 8) & 0xff;
+ countsalt[3] = count & 0xff;
+
+ /* first round, salt is salt */
+ SHA512Init(&ctx);
+ SHA512Update(&ctx, salt, saltlen);
+ SHA512Update(&ctx, countsalt, sizeof(countsalt));
+ SHA512Final(sha2salt, &ctx);
+ bcrypt_hash(sha2pass, sha2salt, tmpout);
+ memcpy(out, tmpout, sizeof(out));
+
+ for (i = 1; i < rounds; i++) {
+ /* subsequent rounds, salt is previous output */
+ SHA512Init(&ctx);
+ SHA512Update(&ctx, tmpout, sizeof(tmpout));
+ SHA512Final(sha2salt, &ctx);
+ bcrypt_hash(sha2pass, sha2salt, tmpout);
+ for (j = 0; j < sizeof(out); j++)
+ out[j] ^= tmpout[j];
+ }
+
+ /*
+ * pbkdf2 deviation: ouput the key material non-linearly.
+ */
+ amt = MIN(amt, keylen);
+ for (i = 0; i < amt; i++)
+ key[i * stride + (count - 1)] = out[i];
+ keylen -= amt;
+ }
+
+ /* zap */
+ memset(&ctx, 0, sizeof(ctx));
+ memset(out, 0, sizeof(out));
+
+ return 0;
+}
diff --git a/lib/libutil/pkcs5_pbkdf2.3 b/lib/libutil/pkcs5_pbkdf2.3
index 3a924e75b15..803e484cf71 100644
--- a/lib/libutil/pkcs5_pbkdf2.3
+++ b/lib/libutil/pkcs5_pbkdf2.3
@@ -1,4 +1,4 @@
-.\" $OpenBSD: pkcs5_pbkdf2.3,v 1.3 2012/09/07 05:48:20 jmc Exp $
+.\" $OpenBSD: pkcs5_pbkdf2.3,v 1.4 2013/06/03 21:07:02 tedu Exp $
.\"
.\" Copyright (c) 2012 Ted Unangst <tedu@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: September 7 2012 $
+.Dd $Mdocdate: June 3 2013 $
.Dt PKCS5_PBKDF2 3
.Os
.Sh NAME
@@ -45,7 +45,8 @@ function returns 0 to indicate success and -1 for failure.
.\" .Sh EXAMPLES
.\" .Sh ERRORS
.Sh SEE ALSO
-.Xr sha1 1
+.Xr sha1 1 ,
+.Xr bcrypt_pbkdf 3
.Sh STANDARDS
.Rs
.%A B. Kaliski
diff --git a/lib/libutil/pkcs5_pbkdf2.c b/lib/libutil/pkcs5_pbkdf2.c
index 17e54c3dc55..26c2b6466fe 100644
--- a/lib/libutil/pkcs5_pbkdf2.c
+++ b/lib/libutil/pkcs5_pbkdf2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pkcs5_pbkdf2.c,v 1.3 2012/09/09 18:08:21 matthew Exp $ */
+/* $OpenBSD: pkcs5_pbkdf2.c,v 1.4 2013/06/03 21:07:02 tedu Exp $ */
/*-
* Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr>
@@ -73,11 +73,11 @@ hmac_sha1(const u_int8_t *text, size_t text_len, const u_int8_t *key,
* Code based on IEEE Std 802.11-2007, Annex H.4.2.
*/
int
-pkcs5_pbkdf2(const char *pass, size_t pass_len, const char *salt, size_t salt_len,
- u_int8_t *key, size_t key_len, unsigned int rounds)
+pkcs5_pbkdf2(const char *pass, size_t pass_len, const uint8_t *salt, size_t salt_len,
+ uint8_t *key, size_t key_len, unsigned int rounds)
{
- u_int8_t *asalt, obuf[SHA1_DIGEST_LENGTH];
- u_int8_t d1[SHA1_DIGEST_LENGTH], d2[SHA1_DIGEST_LENGTH];
+ uint8_t *asalt, obuf[SHA1_DIGEST_LENGTH];
+ uint8_t d1[SHA1_DIGEST_LENGTH], d2[SHA1_DIGEST_LENGTH];
unsigned int i, j;
unsigned int count;
size_t r;
diff --git a/lib/libutil/shlib_version b/lib/libutil/shlib_version
index f6b149e5862..18279d15316 100644
--- a/lib/libutil/shlib_version
+++ b/lib/libutil/shlib_version
@@ -1,2 +1,2 @@
major=11
-minor=4
+minor=5
diff --git a/lib/libutil/util.h b/lib/libutil/util.h
index 92e3c45ed20..469e003d587 100644
--- a/lib/libutil/util.h
+++ b/lib/libutil/util.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: util.h,v 1.33 2012/12/05 23:20:06 deraadt Exp $ */
+/* $OpenBSD: util.h,v 1.34 2013/06/03 21:07:02 tedu Exp $ */
/* $NetBSD: util.h,v 1.2 1996/05/16 07:00:22 thorpej Exp $ */
/*-
@@ -113,8 +113,10 @@ int uu_unlock(const char *);
int fmt_scaled(long long, char *);
int scan_scaled(char *, long long *);
int isduid(const char *, int);
-int pkcs5_pbkdf2(const char *, size_t, const char *, size_t,
- u_int8_t *, size_t, unsigned int);
+int pkcs5_pbkdf2(const char *, size_t, const uint8_t *, size_t,
+ uint8_t *, size_t, unsigned int);
+int bcrypt_pbkdf(const char *, size_t, const uint8_t *, size_t,
+ uint8_t *, size_t, unsigned int);
__END_DECLS