From 23f70ce95419258604c91ecc946b1164954ba990 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20M=C3=B6ller?= <nisse@lysator.liu.se>
Date: Tue, 25 Jun 2013 13:47:08 +0200
Subject: [PATCH] New program nettle-pbkdf2.

---
 ChangeLog                    |   6 ++
 testsuite/Makefile.in        |   2 +-
 testsuite/nettle-pbkdf2-test |  29 ++++++
 tools/Makefile.in            |  10 +-
 tools/nettle-pbkdf2.c        | 180 +++++++++++++++++++++++++++++++++++
 5 files changed, 224 insertions(+), 3 deletions(-)
 create mode 100755 testsuite/nettle-pbkdf2-test
 create mode 100644 tools/nettle-pbkdf2.c

diff --git a/ChangeLog b/ChangeLog
index d6327ab7..a7af3865 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2013-06-25  Niels Möller  <nisse@lysator.liu.se>
 
+	* tools/nettle-pbkdf2.c: New command line tool.
+	* tools/Makefile.in (TARGETS): Added nettle-pbkdf2.
+	(nettle-pbkdf2$(EXEEXT)): New target.
+	* testsuite/nettle-pbkdf2-test: New test case.
+	* testsuite/Makefile.in (TS_SH): Added nettle-pbkdf2-test.
+
 	* tools/nettle-hash.c (digest_file): Use stack allocation for the
 	small hex output buffer.
 
diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in
index 91f6e2a3..bf0e53c6 100644
--- a/testsuite/Makefile.in
+++ b/testsuite/Makefile.in
@@ -47,7 +47,7 @@ TS_HOGWEED = $(TS_HOGWEED_SOURCES:.c=$(EXEEXT))
 TS_C = $(TS_NETTLE) @IF_HOGWEED@ $(TS_HOGWEED)
 TS_CXX = @IF_CXX@ $(CXX_SOURCES:.cxx=$(EXEEXT))
 TARGETS = $(TS_C) $(TS_CXX)
-TS_SH = sexp-conv-test pkcs1-conv-test symbols-test
+TS_SH = sexp-conv-test pkcs1-conv-test nettle-pbkdf2-test symbols-test
 TS_ALL = $(TARGETS) $(TS_SH)
 EXTRA_SOURCES = sha1-huge-test.c
 EXTRA_TARGETS = $(EXTRA_SOURCES:.c=$(EXEEXT))
diff --git a/testsuite/nettle-pbkdf2-test b/testsuite/nettle-pbkdf2-test
new file mode 100755
index 00000000..a1e26380
--- /dev/null
+++ b/testsuite/nettle-pbkdf2-test
@@ -0,0 +1,29 @@
+#! /bin/sh
+
+if [ -z "$srcdir" ] ; then
+  srcdir=`pwd`
+fi
+
+test_pbkdf2 () {
+    password="$1"
+    salt="$2"
+    iters="$3"
+    expected="$4"
+    length=`expr ${#expected} / 2`
+
+    printf "%s" "$password" | $EMULATOR ../tools/nettle-pbkdf2 \
+	-i "$iters" -l "$length" "$salt" > test1.out
+    echo "$expected" > test2.out
+
+    if cmp test1.out test2.out ; then
+	true
+    else
+	exit 1;
+    fi
+}
+
+test_pbkdf2 passwd salt 1 "55ac046e56e3089f ec1691c22544b605"
+test_pbkdf2 Password NaCl 80000 "4ddcd8f60b98be21 830cee5ef22701f9"
+
+exit 0
+
diff --git a/tools/Makefile.in b/tools/Makefile.in
index 29d55e88..f7b33529 100644
--- a/tools/Makefile.in
+++ b/tools/Makefile.in
@@ -18,7 +18,8 @@ PRE_CPPFLAGS = -I.. -I$(top_srcdir)
 PRE_LDFLAGS = -L..
 
 HOGWEED_TARGETS = pkcs1-conv$(EXEEXT)
-TARGETS = sexp-conv$(EXEEXT) nettle-hash$(EXEEXT) nettle-lfib-stream$(EXEEXT) \
+TARGETS = sexp-conv$(EXEEXT) nettle-hash$(EXEEXT) nettle-pbkdf2$(EXEEXT) \
+	  nettle-lfib-stream$(EXEEXT) \
 	  @IF_HOGWEED@ $(HOGWEED_TARGETS)
 
 all: $(TARGETS)
@@ -28,6 +29,7 @@ getopt_OBJS = ../getopt.$(OBJEXT) ../getopt1.$(OBJEXT)
 sexp_conv_SOURCES = sexp-conv.c input.c output.c parse.c misc.c
 pkcs1_conv_SOURCES = pkcs1-conv.c misc.c
 nettle_hash_SOURCES = nettle-hash.c misc.c
+nettle_pbkdf2_SOURCES = nettle-pbkdf2.c misc.c
 
 SOURCES = $(sexp_conv_SOURCES) nettle-hash.c nettle-lfib-stream.c pkcs1-conv.c
 
@@ -47,7 +49,11 @@ pkcs1-conv$(EXEEXT): $(pkcs1_conv_OBJS) ../libnettle.a ../libhogweed.a
 # FIXME: Avoid linking with gmp
 nettle_hash_OBJS = $(nettle_hash_SOURCES:.c=.$(OBJEXT)) $(getopt_OBJS)
 nettle-hash$(EXEEXT): $(nettle_hash_OBJS) ../libnettle.a
-	$(LINK) $(nettle_hash_OBJS) -lnettle $(LIBS) -o $@
+	$(LINK) $(nettle_hash_OBJS) -lnettle -o $@
+
+nettle_pbkdf2_OBJS = $(nettle_pbkdf2_SOURCES:.c=.$(OBJEXT)) $(getopt_OBJS)
+nettle-pbkdf2$(EXEEXT): $(nettle_pbkdf2_OBJS) ../libnettle.a
+	$(LINK) $(nettle_pbkdf2_OBJS) -lnettle -o $@
 
 
 .c.$(OBJEXT):
diff --git a/tools/nettle-pbkdf2.c b/tools/nettle-pbkdf2.c
new file mode 100644
index 00000000..35c98707
--- /dev/null
+++ b/tools/nettle-pbkdf2.c
@@ -0,0 +1,180 @@
+/* nettle-pbkdf2.c
+ *
+ * Command-line tool for pbkdf2 hashing. */
+
+/* nettle, low-level cryptographics library
+ *
+ * Copyright (C) 2013 Niels Möller
+ *  
+ * The nettle library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ * 
+ * The nettle library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the nettle library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02111-1301, USA.
+ */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pbkdf2.h"
+#include "base16.h"
+
+#include "getopt.h"
+#include "misc.h"
+
+#define DEFAULT_ITERATIONS 10000
+#define DEFAULT_LENGTH 16
+static void
+usage (FILE *f)
+{
+  fprintf(f, "nettle-pbkdf2 [OPTIONS] SALT\n"
+	  "Options:\n"
+	  "  --help                 Show this help.\n"
+	  "  -V, --version          Show version information.\n"
+	  "  -i, --iterations=COUNT Desired iteration count (default %d).\n"
+	  "  -l, --length=LENGTH    Desired output length (octets, default %d)\n"
+	  "  --raw                  Raw binary output.\n"
+	  "  --hex-salt             Use hex encoding for the salt.\n",
+	  DEFAULT_ITERATIONS, DEFAULT_LENGTH);
+}
+
+#define MAX_PASSWORD 1024
+
+int
+main (int argc, char **argv)
+{
+  unsigned iterations = DEFAULT_ITERATIONS;
+  unsigned output_length = DEFAULT_LENGTH;
+  char password[MAX_PASSWORD];
+  size_t password_length;
+  char *output;
+  size_t salt_length;
+  char *salt;
+  int raw = 0;
+  int hex_salt = 0;
+  int c;
+
+  enum { OPT_HELP = 0x300, OPT_RAW, OPT_HEX_SALT };
+  static const struct option options[] =
+    {
+      /* Name, args, flag, val */
+      { "help", no_argument, NULL, OPT_HELP },
+      { "version", no_argument, NULL, 'V' },
+      { "length", required_argument, NULL, 'l' },
+      { "iterations", required_argument, NULL, 'i' },
+      { "raw", no_argument, NULL, OPT_RAW },
+      { "hex-salt", no_argument, NULL, OPT_HEX_SALT },
+
+      { NULL, 0, NULL, 0 }
+    };
+
+  while ( (c = getopt_long(argc, argv, "Vl:i:", options, NULL)) != -1)
+    switch (c)
+      {
+      default:
+	abort();
+      case OPT_HELP:
+	usage (stdout);
+	return EXIT_SUCCESS;
+      case 'V':
+	printf("nettle-pbkdf2 (" PACKAGE_STRING ")\n");
+	return EXIT_SUCCESS;
+      case 'l':
+	{
+	  int arg;
+	  arg = atoi (optarg);
+	  if (arg <= 0)
+	    die ("Invalid length argument: `%s'\n", optarg);
+
+	  output_length = arg;
+	}
+	break;
+      case 'i':
+	{
+	  int arg;
+	  arg = atoi (optarg);
+	  if (arg <= 0)
+	    die ("Invalid iteration count: `%s'\n", optarg);
+	  iterations = arg;
+	}
+	break;
+      case OPT_RAW:
+	raw = 1;
+	break;
+      case OPT_HEX_SALT:
+	hex_salt = 1;
+	break;
+      }
+  argv += optind;
+  argc -= optind;
+
+  if (argc != 1)
+    {
+      usage (stderr);
+      return EXIT_FAILURE;
+    }
+
+  salt = strdup (argv[0]);
+  salt_length = strlen(salt);
+  
+  if (hex_salt)
+    {
+      struct base16_decode_ctx base16;
+
+      base16_decode_init (&base16);
+      if (!base16_decode_update (&base16,
+				 &salt_length,
+				 salt, salt_length, salt)
+	  || !base16_decode_final (&base16))
+	die ("Invalid salt (expecting hex encoding).\n");
+    }
+  
+  password_length = fread (password, 1, sizeof(password), stdin);
+  if (password_length == sizeof(password))
+    die ("Password input to long. Current limit is %d characters.\n",
+	 (int) sizeof(password) - 1);
+  if (ferror (stdin))
+    die ("Reading password input failed: %s.\n", strerror (errno));
+
+  output = xalloc (output_length);
+  pbkdf2_hmac_sha256 (password_length, password, iterations, salt_length, salt,
+		      output_length, output);
+
+  free (salt);
+
+  if (raw)
+    fwrite (output, output_length, 1, stdout);
+  else
+    {
+      unsigned i;
+      char hex[BASE16_ENCODE_LENGTH(8) + 1];
+      for (i = 0; i + 8 < output_length; i += 8)
+	{
+	  base16_encode_update(hex, 8, output + i);
+	  hex[BASE16_ENCODE_LENGTH(8)] = 0;
+	  printf("%s%c", hex, i % 64 == 56 ? '\n' : ' ');
+	}
+      base16_encode_update(hex, output_length - i, output + i);
+      hex[BASE16_ENCODE_LENGTH(output_length - i)] = 0;
+      printf("%s\n", hex);
+    }
+  if (fflush(stdout) != 0 )
+    die("Write failed: %s\n", STRERROR(errno));
+
+  return EXIT_SUCCESS;
+}
-- 
GitLab