From 6649e8dffcbff86fdb77c2ca4f160e6688966846 Mon Sep 17 00:00:00 2001
From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com>
Date: Sun, 7 Jul 2024 22:28:49 +0200
Subject: [PATCH] Add tcl pbkdf2 function
Patch by: michaelortmann
Useful for tcl scripts that add SASL SCRAM mechanism which, when implemented in Tcl, are very slow. This PR will help with a fast replacement function exported by eggdrop.
The new tcl function pbkdf2() returns as hexadecimal string by default and -bin by option, which is similar, to what tcllibs sha256() does (older tcllibs md5 had it the other way around), see https://core.tcl-lang.org/tcllib/doc/trunk/embedded/md/tcllib/files/modules/sha1/sha256.md
---
doc/sphinx_source/using/tcl-commands.rst | 20 +++++++
src/mod/pbkdf2.mod/pbkdf2.c | 18 +-----
src/mod/pbkdf2.mod/tclpbkdf2.c | 70 ++++++++++++++++++++++++
3 files changed, 92 insertions(+), 16 deletions(-)
create mode 100644 src/mod/pbkdf2.mod/tclpbkdf2.c
diff --git a/doc/sphinx_source/using/tcl-commands.rst b/doc/sphinx_source/using/tcl-commands.rst
index 6a662052c..339c728c3 100644
--- a/doc/sphinx_source/using/tcl-commands.rst
+++ b/doc/sphinx_source/using/tcl-commands.rst
@@ -2200,6 +2200,26 @@ setflags
[ [channel]]
Module: filesys
+PBKDF2 Module
+-------------
+
+^^^^^^^^^^^^^^^
+encpass2
+^^^^^^^^^^^^^^^
+
+
+ Returns: a hash in the format of "$pbkdf2-$rounds=$$" where digest is the digest set in the config variable pbkdf2-method, rounds is the number of rounds set in the config variable pbkdf2-rounds, salt is the base64 salt used to generate the hash, and hash is the generated base64 hash.
+
+ Module: pbkdf2
+
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+pbkdf2 [-bin]
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ Returns: a derived key from the provided "pass" string using "salt" and "rounds" count as specified in RFC 2898 as a hexadecimal string. Using the optional -bin flag will return the result as binary data.
+
+ Module: pbkdf2
+
Miscellaneous Commands
----------------------
diff --git a/src/mod/pbkdf2.mod/pbkdf2.c b/src/mod/pbkdf2.mod/pbkdf2.c
index 169d92bc8..2eda85038 100644
--- a/src/mod/pbkdf2.mod/pbkdf2.c
+++ b/src/mod/pbkdf2.mod/pbkdf2.c
@@ -8,6 +8,7 @@
*/
#include "src/mod/module.h"
+#include "src/mod/pbkdf2.mod/tclpbkdf2.c"
#if OPENSSL_VERSION_NUMBER >= 0x1000000fL /* 1.0.0 */
#define MODULE_NAME "encryption2"
@@ -108,7 +109,7 @@ static char *pbkdf2_hash(const char *pass, const char *digest_name,
digestlen, buf)) {
explicit_bzero(buf, digestlen);
explicit_bzero(out, outlen);
- putlog(LOG_MISC, "*", "PBKDF2 error: PKCS5_PBKDF2_HMAC(): %s.",
+ putlog(LOG_MISC, "*", "PBKDF2 key derivation error: %s.",
ERR_error_string(ERR_get_error(), NULL));
nfree(buf);
return NULL;
@@ -223,21 +224,6 @@ static char *pbkdf2_verify(const char *pass, const char *encrypted)
return (char *) encrypted;
}
-static int tcl_encpass2 STDVAR
-{
- BADARGS(2, 2, " string");
- if (strlen(argv[1]) > 0)
- Tcl_AppendResult(irp, pbkdf2_encrypt(argv[1]), NULL);
- else
- Tcl_AppendResult(irp, "", NULL);
- return TCL_OK;
-}
-
-static tcl_cmds my_tcl_cmds[] = {
- {"encpass2", tcl_encpass2},
- {NULL, NULL}
-};
-
static tcl_ints my_tcl_ints[] = {
{"pbkdf2-re-encode", &pbkdf2_re_encode, 0},
{"pbkdf2-rounds", &pbkdf2_rounds, 0},
diff --git a/src/mod/pbkdf2.mod/tclpbkdf2.c b/src/mod/pbkdf2.mod/tclpbkdf2.c
new file mode 100644
index 000000000..8fa7bf8dd
--- /dev/null
+++ b/src/mod/pbkdf2.mod/tclpbkdf2.c
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tclpbkdf2.c -- tcl functions for pbkdf2.mod
+ *
+ * Written by thommey and Michael Ortmann
+ *
+ * Copyright (C) 2017 - 2024 Eggheads Development Team
+ */
+
+#include
+#include
+
+static char *pbkdf2_encrypt(const char *);
+
+static int tcl_encpass2 STDVAR
+{
+ BADARGS(2, 2, " string");
+ Tcl_SetResult(irp, pbkdf2_encrypt(argv[1]), TCL_STATIC);
+ return TCL_OK;
+}
+
+static int tcl_pbkdf2 STDVAR
+{
+ int hex, digestlen, i;
+ unsigned int rounds;
+ const EVP_MD *digest;
+ unsigned char buf[256];
+ char buf_hex[256];
+ Tcl_Obj *result = 0;
+
+ BADARGS(5, 6, " ?-bin? pass salt rounds digest");
+ if (argc == 6) {
+ if (!strcmp(argv[1], "-bin"))
+ hex = 0;
+ else {
+ Tcl_AppendResult(irp, "bad option ", argv[1], ": must be -bin", NULL);
+ return TCL_ERROR;
+ }
+ }
+ else
+ hex = 1;
+ rounds = atoi(argv[3 + !hex]);
+ digest = EVP_get_digestbyname(argv[4 + !hex]);
+ if (!digest) {
+ Tcl_AppendResult(irp, "PBKDF2 error: Unknown message digest '", argv[4 + !hex], "'.", NULL);
+ return TCL_ERROR;
+ }
+ digestlen = EVP_MD_size(digest);
+ if (!PKCS5_PBKDF2_HMAC(argv[1 + !hex], strlen(argv[1 + !hex]), (const unsigned char *) argv[2+ !hex], strlen(argv[2 + !hex]), rounds, digest, digestlen, buf)) {
+ Tcl_AppendResult(irp, "PBKDF2 key derivation error: ", ERR_error_string(ERR_get_error(), NULL), ".", NULL);
+ return TCL_ERROR;
+ }
+ if (hex) {
+ for (i = 0; i < digestlen; i++)
+ sprintf(buf_hex + (i * 2), "%.2X", buf[i]);
+ result = Tcl_NewByteArrayObj((unsigned char *) buf_hex, digestlen * 2);
+ explicit_bzero(buf_hex, digestlen * 2);
+ }
+ else
+ result = Tcl_NewByteArrayObj(buf, digestlen);
+ explicit_bzero(buf, digestlen);
+ Tcl_SetObjResult(irp, result);
+ return TCL_OK;
+}
+
+static tcl_cmds my_tcl_cmds[] = {
+ {"encpass2", tcl_encpass2},
+ {"pbkdf2", tcl_pbkdf2},
+ {NULL, NULL}
+};