aboutsummaryrefslogtreecommitdiff
path: root/src/util/crypto_hkdf.c
diff options
context:
space:
mode:
authorBertrand Marc <beberking@gmail.com>2012-05-02 21:43:37 +0200
committerBertrand Marc <beberking@gmail.com>2012-05-02 21:43:37 +0200
commit2b81464a43485fcc8ce079fafdee7b7a171835f4 (patch)
tree394774c0f735199b57d51a2d3840356317853fe1 /src/util/crypto_hkdf.c
Imported Upstream version 0.9.2upstream/0.9.2
Diffstat (limited to 'src/util/crypto_hkdf.c')
-rw-r--r--src/util/crypto_hkdf.c299
1 files changed, 299 insertions, 0 deletions
diff --git a/src/util/crypto_hkdf.c b/src/util/crypto_hkdf.c
new file mode 100644
index 0000000..40bfa67
--- /dev/null
+++ b/src/util/crypto_hkdf.c
@@ -0,0 +1,299 @@
+/*
+ Copyright (c) 2010 Nils Durner
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+/**
+ * @file src/util/crypto_hkdf.c
+ * @brief Hash-based KDF as defined in RFC 5869
+ * @see http://www.rfc-editor.org/rfc/rfc5869.txt
+ * @todo remove GNUNET references
+ * @author Nils Durner
+ *
+ * The following list of people have reviewed this code and considered
+ * it correct on the date given (if you reviewed it, please
+ * have your name added to the list):
+ *
+ * - Christian Grothoff (08.10.2010)
+ * - Nathan Evans (08.10.2010)
+ * - Matthias Wachs (08.10.2010)
+ */
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+/**
+ * Set this to 0 if you compile this code outside of GNUnet.
+ */
+#define GNUNET_BUILD 1
+
+/**
+ * Enable debugging.
+ */
+#define DEBUG_HKDF 0
+
+
+#if GNUNET_BUILD
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#else
+#define GNUNET_NO 0
+#define GNUNET_YES 1
+#define GNUNET_SYSERR -1
+#include <stdlib.h>
+#endif
+
+#include <gcrypt.h>
+
+
+/**
+ * @brief Compute the HMAC
+ * @todo use chunked buffers
+ * @param mac gcrypt MAC handle
+ * @param key HMAC key
+ * @param key_len length of key
+ * @param buf message to be processed
+ * @param buf_len length of buf
+ * @return HMAC, freed by caller via gcry_md_close/_reset
+ */
+static const void *
+doHMAC (gcry_md_hd_t mac, const void *key, size_t key_len, const void *buf,
+ size_t buf_len)
+{
+ gcry_md_setkey (mac, key, key_len);
+ gcry_md_write (mac, buf, buf_len);
+
+ return (const void *) gcry_md_read (mac, 0);
+}
+
+/**
+ * @brief Generate pseudo-random key
+ * @param mac gcrypt HMAC handle
+ * @param xts salt
+ * @param xts_len length of the salt
+ * @param skm source key material
+ * @param skm_len length of skm
+ * @param prk result buffer (allocated by caller; at least gcry_md_dlen() bytes)
+ * @return GNUNET_YES on success
+ */
+static int
+getPRK (gcry_md_hd_t mac, const void *xts, size_t xts_len, const void *skm,
+ size_t skm_len, void *prk)
+{
+ const void *ret;
+
+ ret = doHMAC (mac, xts, xts_len, skm, skm_len);
+ if (ret == NULL)
+ return GNUNET_SYSERR;
+ memcpy (prk, ret, gcry_md_get_algo_dlen (gcry_md_get_algo (mac)));
+
+ return GNUNET_YES;
+}
+
+
+#if DEBUG_HKDF
+static void
+dump (const char *src, const void *p, unsigned int l)
+{
+ unsigned int i;
+
+ printf ("\n%s: ", src);
+ for (i = 0; i < l; i++)
+ {
+ printf ("%2x", (int) ((const unsigned char *) p)[i]);
+ }
+ printf ("\n");
+}
+#endif
+
+
+/**
+ * @brief Derive key
+ * @param result buffer for the derived key, allocated by caller
+ * @param out_len desired length of the derived key
+ * @param xtr_algo hash algorithm for the extraction phase, GCRY_MD_...
+ * @param prf_algo hash algorithm for the expansion phase, GCRY_MD_...
+ * @param xts salt
+ * @param xts_len length of xts
+ * @param skm source key material
+ * @param skm_len length of skm
+ * @param argp va_list of void * & size_t pairs for context chunks
+ * @return GNUNET_YES on success
+ */
+int
+GNUNET_CRYPTO_hkdf_v (void *result, size_t out_len, int xtr_algo, int prf_algo,
+ const void *xts, size_t xts_len, const void *skm,
+ size_t skm_len, va_list argp)
+{
+ const void *hc;
+ unsigned long i, t, d;
+ unsigned int k = gcry_md_get_algo_dlen (prf_algo);
+ unsigned int xtr_len = gcry_md_get_algo_dlen (xtr_algo);
+ char prk[xtr_len];
+ int ret;
+ gcry_md_hd_t xtr, prf;
+ size_t ctx_len;
+ va_list args;
+
+ if (k == 0)
+ return GNUNET_SYSERR;
+
+ if (gcry_md_open (&xtr, xtr_algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
+ return GNUNET_SYSERR;
+
+ if (gcry_md_open (&prf, prf_algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
+ {
+ gcry_md_close (xtr);
+ return GNUNET_SYSERR;
+ }
+
+ va_copy (args, argp);
+
+ ctx_len = 0;
+ while (NULL != va_arg (args, void *))
+ ctx_len += va_arg (args, size_t);
+
+ va_end (args);
+
+ memset (result, 0, out_len);
+ if (getPRK (xtr, xts, xts_len, skm, skm_len, prk) != GNUNET_YES)
+ goto hkdf_error;
+#if DEBUG_HKDF
+ dump ("PRK", prk, xtr_len);
+#endif
+
+ t = out_len / k;
+ d = out_len % k;
+
+ /* K(1) */
+ {
+ size_t plain_len = k + ctx_len + 1;
+ char plain[plain_len];
+ const void *ctx;
+ char *dst;
+
+ dst = plain + k;
+ va_copy (args, argp);
+ while ((ctx = va_arg (args, void *)))
+ {
+ size_t len;
+
+ len = va_arg (args, size_t);
+ memcpy (dst, ctx, len);
+ dst += len;
+ }
+ va_end (args);
+
+ if (t > 0)
+ {
+ memset (plain + k + ctx_len, 1, 1);
+#if DEBUG_HKDF
+ dump ("K(1)", plain, plain_len);
+#endif
+ hc = doHMAC (prf, prk, xtr_len, &plain[k], ctx_len + 1);
+ if (hc == NULL)
+ goto hkdf_error;
+ memcpy (result, hc, k);
+ result += k;
+ }
+
+ /* K(i+1) */
+ for (i = 1; i < t; i++)
+ {
+ memcpy (plain, result - k, k);
+ memset (plain + k + ctx_len, i + 1, 1);
+ gcry_md_reset (prf);
+#if DEBUG_HKDF
+ dump ("K(i+1)", plain, plain_len);
+#endif
+ hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
+ if (hc == NULL)
+ goto hkdf_error;
+ memcpy (result, hc, k);
+ result += k;
+ }
+
+ /* K(t):d */
+ if (d > 0)
+ {
+ if (t > 0)
+ {
+ memcpy (plain, result - k, k);
+ i++;
+ }
+ memset (plain + k + ctx_len, i, 1);
+ gcry_md_reset (prf);
+#if DEBUG_HKDF
+ dump ("K(t):d", plain, plain_len);
+#endif
+ if (t > 0)
+ hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
+ else
+ hc = doHMAC (prf, prk, xtr_len, plain + k, plain_len - k);
+ if (hc == NULL)
+ goto hkdf_error;
+ memcpy (result, hc, d);
+ }
+#if DEBUG_HKDF
+ dump ("result", result - k, out_len);
+#endif
+
+ ret = GNUNET_YES;
+ goto hkdf_ok;
+ }
+hkdf_error:
+ ret = GNUNET_SYSERR;
+hkdf_ok:
+ gcry_md_close (prf);
+ gcry_md_close (xtr);
+
+ return ret;
+}
+
+
+/**
+ * @brief Derive key
+ * @param result buffer for the derived key, allocated by caller
+ * @param out_len desired length of the derived key
+ * @param xtr_algo hash algorithm for the extraction phase, GCRY_MD_...
+ * @param prf_algo hash algorithm for the expansion phase, GCRY_MD_...
+ * @param xts salt
+ * @param xts_len length of xts
+ * @param skm source key material
+ * @param skm_len length of skm
+ * @return GNUNET_YES on success
+ */
+int
+GNUNET_CRYPTO_hkdf (void *result, size_t out_len, int xtr_algo, int prf_algo,
+ const void *xts, size_t xts_len, const void *skm,
+ size_t skm_len, ...)
+{
+ va_list argp;
+ int ret;
+
+ va_start (argp, skm_len);
+ ret =
+ GNUNET_CRYPTO_hkdf_v (result, out_len, xtr_algo, prf_algo, xts, xts_len,
+ skm, skm_len, argp);
+ va_end (argp);
+
+ return ret;
+}
+
+/* end of crypto_hkdf.c */