Hello,

It is based on this paper https://eprint.iacr.org/2016/027.pdf
Thank you for your review. I have updated the patch.

1. balloon_set_hash_alg() function is gone. Now the user provides a hash
algorithm via argument.
2. I have changed the helper functions a bit and I used stack allocation
for all local variables, no more heap allocation.
3. Now the user has to provide a working buffer via argument. I have added
the function balloon_buf_size() that returns the necessary size of the
buffer.

Attaching updated patch.

Kind regards,
Zoltan

On Fri, Aug 19, 2022 at 10:06 AM Niels Möller <ni...@lysator.liu.se> wrote:

> Zoltan Fridrich <zfrid...@redhat.com> writes:
>
> > I would like to contribute an implementation of the balloon hashing
> > algorithm to Nettle.
>
> Please provide some information about use cases and which specification
> it is based on. (I only had a quick look at wikipedia and the
> https://eprint.iacr.org/2016/027 paper, to get some context).
>
> > I am aware that the code does not have proper Nettle formatting, I will
> > change that after the patch will be in an acceptable state. Could you
> > please provide feedback on the patch? Thank you.
>
> A few initial comments:
>
> 1. The balloon_set_hash_alg implies global state, which isn't good in a
>    library. It's better to pass the const struct nettle_hash *hash_alg
>    as an argument to the balloon function.
>
> 2. For allocating smallish items like the hashing context, use stack
>    allocation. Using the TMP_DECL, TMP_ALLOC macros (when size is
>    bounded but determined at runtime), of if constant, a plain
>
>       uint8_t data[3*sizeof(i)];
>
>    instead of
>
>       uint8_t *data = xalloc(3 * sizeof(i));
>
>    For the hashing context, if only one is needed, it would be better to
>    allocate in the top-level baloon function, instead of allocating and
>    freeing in the hash utility function.
>
>    Or in this particular case, one could also consider using
>
>      uint64_t data[3];
>
>    plus endian-dependent byteswapping when writing the values. As I
>    understand it, if this part isn't performance critical, style should
>    be chosen based on what makes the code clearest.
>
> 3. For allocating the working storage, which as I understand it can be
>    intentionally pretty large, using xalloc isn't so nice. It would be
>    more inline with nettle design to leave to the application to
>    allocate the storage and pass in. You can then provide a function or
>    macro the application can use to determine needed size.
>
> Regards,
> /Niels
>
> --
> Niels Möller. PGP key CB4962D070D77D7FCB8BA36271D8F1FF368C6677.
> Internet email is subject to wholesale government surveillance.
>
>
diff --color -ruNp a/balloon.c b/balloon.c
--- a/balloon.c	1970-01-01 01:00:00.000000000 +0100
+++ b/balloon.c	2022-08-19 11:11:11.591188729 +0200
@@ -0,0 +1,127 @@
+/* balloon.c - Balloon password-hashing algorithm
+
+   Copyright (C) 2022 Zoltan Fridrich
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle 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
+   General Public License for more details.
+ 
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#include "balloon.h"
+#include "macros.h"
+#include "nettle-internal.h"
+
+#define DELTA 3
+
+static inline void
+hash(const struct nettle_hash *alg,
+     uint64_t cnt,
+     size_t a_len, const uint8_t *a,
+     size_t b_len, const uint8_t *b,
+     uint8_t *result)
+{
+    uint8_t ctx[NETTLE_MAX_HASH_CONTEXT_SIZE];
+    uint8_t tmp[sizeof(uint64_t)];
+
+    alg->init(ctx);
+    LE_WRITE_UINT64(tmp, cnt);
+    alg->update(ctx, sizeof(tmp), tmp);
+    alg->update(ctx, a_len, a);
+    alg->update(ctx, b_len, b);
+    alg->digest(ctx, alg->digest_size, result);
+}
+
+static inline void
+hash_ints(const struct nettle_hash *alg,
+          uint64_t i, uint64_t j, uint64_t k, uint8_t *result)
+{
+    uint8_t ctx[NETTLE_MAX_HASH_CONTEXT_SIZE];
+    uint8_t tmp[sizeof(uint64_t)];
+
+    alg->init(ctx);
+    LE_WRITE_UINT64(tmp, i);
+    alg->update(ctx, sizeof(tmp), tmp);
+    LE_WRITE_UINT64(tmp, j);
+    alg->update(ctx, sizeof(tmp), tmp);
+    LE_WRITE_UINT64(tmp, k);
+    alg->update(ctx, sizeof(tmp), tmp);
+    alg->digest(ctx, alg->digest_size, result);
+}
+
+static inline unsigned
+block_to_int(size_t length, const uint8_t *block, unsigned mod)
+{
+    unsigned r = 0;
+
+    for (int i = length - 1; i >= 0; --i) {
+        r <<= 8;
+        r += (block[i] & 0xFF);
+        r %= mod;
+    }
+    return r;
+}
+
+void
+balloon(size_t passwd_len, const uint8_t *passwd,
+        size_t salt_len, const uint8_t *salt,
+        unsigned s_cost, unsigned t_cost, 
+        const struct nettle_hash *alg,
+        uint8_t *buf, uint8_t *result)
+{
+    uint8_t block[NETTLE_MAX_HASH_DIGEST_SIZE];
+    const unsigned BS = alg->digest_size;
+    unsigned i, j, k, cnt = 0;
+
+    hash(alg, cnt++, passwd_len, passwd, salt_len, salt, buf);
+    for (i = 1; i < s_cost; ++i)
+        hash(alg, cnt++, BS, buf + (i - 1) * BS, 0, NULL, buf + i * BS);
+
+    for (i = 0; i < t_cost; ++i) {
+        for (j = 0; j < s_cost; ++j) {
+            hash(alg, cnt++, BS, buf + (j ? j - 1 : s_cost - 1) * BS,
+                 BS, buf + j * BS, buf + j * BS);
+            for (k = 0; k < DELTA; ++k) {
+                hash_ints(alg, i, j, k, block);
+                hash(alg, cnt++, salt_len, salt, BS, block, block);
+                hash(alg, cnt++, BS, buf + j * BS,
+                     BS, buf + block_to_int(BS, block, s_cost) * BS,
+                     buf + j * BS);
+            }
+        }
+    }
+    memcpy(result, buf + (s_cost - 1) * BS, BS);
+}
+
+size_t
+balloon_buf_size(const struct nettle_hash *alg, unsigned s_cost)
+{
+    return s_cost * alg->digest_size;
+}
diff --color -ruNp a/balloon.h b/balloon.h
--- a/balloon.h	1970-01-01 01:00:00.000000000 +0100
+++ b/balloon.h	2022-08-19 11:00:04.057815450 +0200
@@ -0,0 +1,59 @@
+/* balloon.h - Balloon password-hashing algorithm
+
+   Copyright (C) 2022 Zoltan Fridrich
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle 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
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+ 
+#ifndef NETTLE_BALLOON_H_INCLUDED
+#define NETTLE_BALLOON_H_INCLUDED
+
+#include "nettle-meta.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Name mangling */
+#define balloon nettle_balloon
+#define balloon_buf_size nettle_balloon_buf_size
+
+void
+balloon(size_t passwd_len, const uint8_t *passwd,
+        size_t salt_len, const uint8_t *salt,
+        unsigned s_cost, unsigned t_cost,
+        const struct nettle_hash *alg,
+        uint8_t *buf, uint8_t *result);
+
+size_t
+balloon_buf_size(const struct nettle_hash *alg, unsigned s_cost);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NETTLE_BALLOON_H_INCLUDED */
diff --color -ruNp a/Makefile.in b/Makefile.in
--- a/Makefile.in	2022-08-03 15:22:54.207789385 +0200
+++ b/Makefile.in	2022-08-18 16:18:39.330463813 +0200
@@ -82,7 +82,7 @@ nettle_SOURCES = aes-decrypt-internal.c
 		 aes256-meta.c \
 		 nist-keywrap.c \
 		 arcfour.c arcfour-crypt.c \
-		 arctwo.c arctwo-meta.c blowfish.c blowfish-bcrypt.c \
+		 arctwo.c arctwo-meta.c balloon.c blowfish.c blowfish-bcrypt.c \
 		 base16-encode.c base16-decode.c base16-meta.c \
 		 base64-encode.c base64-decode.c base64-meta.c \
 		 base64url-encode.c base64url-decode.c base64url-meta.c \
@@ -218,7 +218,7 @@ hogweed_SOURCES = sexp.c sexp-format.c \
 
 OPT_SOURCES = fat-arm.c fat-arm64.c fat-ppc.c fat-s390x.c fat-x86_64.c mini-gmp.c
 
-HEADERS = aes.h arcfour.h arctwo.h asn1.h blowfish.h \
+HEADERS = aes.h arcfour.h arctwo.h asn1.h balloon.h blowfish.h \
 	  base16.h base64.h bignum.h buffer.h camellia.h cast128.h \
 	  cbc.h ccm.h cfb.h chacha.h chacha-poly1305.h ctr.h \
 	  curve25519.h curve448.h des.h dsa.h dsa-compat.h eax.h \
diff --color -ruNp a/testsuite/balloon-test.c b/testsuite/balloon-test.c
--- a/testsuite/balloon-test.c	1970-01-01 01:00:00.000000000 +0100
+++ b/testsuite/balloon-test.c	2022-08-19 10:57:18.966504697 +0200
@@ -0,0 +1,75 @@
+/* balloon-test.c
+
+   Copyright (C) 2022 Zoltan Fridrich
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle 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
+   General Public License for more details.
+ 
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+
+#include "testutils.h"
+#include "balloon.h"
+#include "nettle-internal.h"
+
+static inline void
+test_balloon(size_t password_len, const char *password,
+             size_t salt_len, const char *salt,
+             unsigned s_cost, unsigned t_cost,
+             const struct nettle_hash *alg, const struct tstring *expected)
+{
+    uint8_t hash[NETTLE_MAX_HASH_DIGEST_SIZE];
+    uint8_t *buf = xalloc(balloon_buf_size(alg, s_cost));
+
+    balloon(password_len, (const uint8_t *)password,
+            salt_len, (const uint8_t *)salt,
+            s_cost, t_cost, alg, buf, hash);
+
+    if (!MEMEQ(alg->digest_size, hash, expected->data)) {
+        fprintf(stderr, "test_balloon: result doesn't match the expectation:");
+        fprintf(stderr, "\nOutput: ");
+        print_hex(alg->digest_size, hash);
+        fprintf(stderr, "\nExpected:");
+        tstring_print_hex(expected);
+        fprintf(stderr, "\n");
+        free(buf);
+        FAIL();
+    }
+    free(buf);
+}
+
+void
+test_main(void)
+{
+    test_balloon(8, "hunter42", 11, "examplesalt", 1024, 3, &nettle_sha256,
+                 SHEX("716043dff777b44aa7b88dcbab12c078abecfac9d289c5b5195967aa63440dfb"));
+    test_balloon(0, "", 4, "salt", 3, 3, &nettle_sha256,
+                 SHEX("5f02f8206f9cd212485c6bdf85527b698956701ad0852106f94b94ee94577378"));
+    test_balloon(8, "password", 0, "", 3, 3, &nettle_sha256,
+                 SHEX("20aa99d7fe3f4df4bd98c655c5480ec98b143107a331fd491deda885c4d6a6cc"));
+    test_balloon(1, "", 1, "", 3, 3, &nettle_sha256,
+                 SHEX("4fc7e302ffa29ae0eac31166cee7a552d1d71135f4e0da66486fb68a749b73a4"));
+    test_balloon(8, "password", 4, "salt", 1, 1, &nettle_sha256,
+                 SHEX("eefda4a8a75b461fa389c1dcfaf3e9dfacbc26f81f22e6f280d15cc18c417545"));
+}
diff --color -ruNp a/testsuite/.gitignore b/testsuite/.gitignore
--- a/testsuite/.gitignore	2022-08-03 15:22:54.246790154 +0200
+++ b/testsuite/.gitignore	2022-08-03 16:37:44.331951753 +0200
@@ -4,6 +4,7 @@
 /aes-keywrap-test
 /arcfour-test
 /arctwo-test
+/balloon-test
 /base16-test
 /base64-test
 /bignum-test
diff --color -ruNp a/testsuite/Makefile.in b/testsuite/Makefile.in
--- a/testsuite/Makefile.in	2022-08-03 15:22:54.246790154 +0200
+++ b/testsuite/Makefile.in	2022-08-03 16:39:17.725784157 +0200
@@ -11,7 +11,7 @@ PRE_CPPFLAGS = -I.. -I$(top_srcdir)
 PRE_LDFLAGS = -L..
 
 TS_NETTLE_SOURCES = aes-test.c aes-keywrap-test.c arcfour-test.c arctwo-test.c \
-		    blowfish-test.c bcrypt-test.c cast128-test.c \
+		    balloon-test.c blowfish-test.c bcrypt-test.c cast128-test.c \
 	            base16-test.c base64-test.c \
 		    camellia-test.c chacha-test.c \
 		    cnd-memcpy-test.c \
_______________________________________________
nettle-bugs mailing list -- nettle-bugs@lists.lysator.liu.se
To unsubscribe send an email to nettle-bugs-le...@lists.lysator.liu.se

Reply via email to