aboutsummaryrefslogtreecommitdiff
path: root/jwt.c
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi92@disroot.org>2025-10-08 13:50:52 +0200
committerXavier Del Campo Romero <xavi92@disroot.org>2025-10-08 22:55:44 +0200
commit10e42591ac72285736d5cc4ee5e7c2f68dbf1e4b (patch)
tree3bb586177e375a6f7f91c0335876faefc28b805c /jwt.c
parent805630dbfcd409a5d49bc89102f4183b71f713f9 (diff)
downloadslcl-10e42591ac72285736d5cc4ee5e7c2f68dbf1e4b.tar.gz
Replace OpenSSL with libsodium and argon2id
The SHA256-based password hashing algorithm used by slcl(1) and usergen(1) is considered insecure against several kinds of attacks, including brute force attacks. [1] Therefore, a stronger password hashing algorithm based on the Argon2id key derivation function is now used by default. While OpenSSL does support Argon2id, it is only supported by very recent versions [2], which are still not packaged by most distributions as of the time of this writing. [3] As an alternative to OpenSSL, libsodium [4] had several benefits: - It provides easy-to-use functions for password hashing, base64 encoding/decoding and other cryptographic primitives used by slcl(1) and usergen(1). - It is packaged by most distributions [5], and most often only the patch version differs, which ensures good compatibility across distributions. Unfortunately, and as opposed to OpenSSL, libsodium does not come with command-line tools. Therefore, usergen(1) had to be rewritten in C. In order to maintain backwards compatiblity with existing databases, slcl(1) and usergen(1) shall support the insecure, SHA256-based password hashing algorithm. However, Argon2id shall now be the default choice for usergen(1). [1]: https://security.stackexchange.com/questions/195563/why-is-sha-256-not-good-for-passwords [2]: https://docs.openssl.org/3.3/man7/EVP_KDF-ARGON2/ [3]: https://repology.org/project/openssl/versions [4]: https://www.libsodium.org/ [5]: https://repology.org/project/libsodium/versions
Diffstat (limited to 'jwt.c')
-rw-r--r--jwt.c114
1 files changed, 69 insertions, 45 deletions
diff --git a/jwt.c b/jwt.c
index 94f2f6e..fd5adb1 100644
--- a/jwt.c
+++ b/jwt.c
@@ -1,21 +1,17 @@
#include "jwt.h"
-#include "base64.h"
#include <dynstr.h>
-#include <cjson/cJSON.h>
-#include <openssl/evp.h>
-#include <openssl/hmac.h>
-#include <openssl/sha.h>
+#include <sodium.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-static const char jwt_header[] = "{\"alg\": \"HS256\", \"typ\": \"JWT\"}";
+#define VARIANT sodium_base64_VARIANT_URLSAFE
static char *get_payload(const char *const name)
{
- char *ret = NULL;
+ char *ret = NULL, *s = NULL;
struct dynstr d;
dynstr_init(&d);
@@ -25,13 +21,27 @@ static char *get_payload(const char *const name)
fprintf(stderr, "%s: dynstr_append name failed\n", __func__);
goto end;
}
- else if (!(ret = base64_encode(d.str, d.len)))
+
+ const size_t n = sodium_base64_encoded_len(d.len, VARIANT);
+ const unsigned char *const in = (const unsigned char *)d.str;
+
+ if (!(s = malloc(n)))
{
- fprintf(stderr, "%s: base64 failed\n", __func__);
+ fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
goto end;
}
+ else if (!sodium_bin2base64(s, n, in, d.len, VARIANT))
+ {
+ fprintf(stderr, "%s: sodium_bin2base64 failed\n", __func__);
+ goto end;
+ }
+
+ ret = s;
end:
+ if (!ret)
+ free(s);
+
dynstr_free(&d);
return ret;
}
@@ -39,47 +49,66 @@ end:
static char *get_hmac(const void *const buf, const size_t n,
const void *const key, const size_t keyn)
{
- unsigned char hmac[SHA256_DIGEST_LENGTH];
- const EVP_MD *const md = EVP_sha256();
+ unsigned char hmac[crypto_auth_hmacsha256_KEYBYTES];
char *ret = NULL;
- if (!md)
+ if (keyn != crypto_auth_hmacsha256_KEYBYTES)
{
- fprintf(stderr, "%s: EVP_sha256 failed\n", __func__);
- return NULL;
+ fprintf(stderr, "%s: invalid key size (%zu), expected %u\n", __func__,
+ n, crypto_auth_hmacsha256_KEYBYTES);
+ goto failure;
}
- else if (!HMAC(md, key, keyn, buf, n, hmac, NULL))
+ else if (crypto_auth_hmacsha256(hmac, buf, n, key))
{
fprintf(stderr, "%s: HMAC failed\n", __func__);
- return NULL;
+ goto failure;
+ }
+
+ const size_t b64n = sodium_base64_encoded_len(sizeof hmac, VARIANT);
+
+ if (!(ret = malloc(b64n)))
+ {
+ fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
+ goto failure;
}
- else if (!(ret = base64_encode(hmac, sizeof hmac)))
+ else if (!sodium_bin2base64(ret, b64n, hmac, sizeof hmac, VARIANT))
{
- fprintf(stderr, "%s: base64 failed\n", __func__);
- return NULL;
+ fprintf(stderr, "%s: sodium_bin2base64 failed\n", __func__);
+ goto failure;
}
return ret;
+
+failure:
+ free(ret);
+ return NULL;
}
char *jwt_encode(const char *const name, const void *const key, const size_t n)
{
- char *ret = NULL;
- char *const header = base64_encode(jwt_header, strlen(jwt_header)),
- *const payload = get_payload(name),
- *hmac = NULL;
+ static const char jwt_header[] = "{\"alg\": \"HS256\", \"typ\": \"JWT\"}";
struct dynstr jwt;
+ const size_t hlen = strlen(jwt_header),
+ sz = sodium_base64_encoded_len(hlen, VARIANT);
+ char *ret = NULL, *header = NULL, *hmac = NULL,
+ *const payload = get_payload(name);
dynstr_init(&jwt);
- if (!header)
+ if (!payload)
{
- fprintf(stderr, "%s: base64_encode header failed\n", __func__);
+ fprintf(stderr, "%s: get_payload failed\n", __func__);
goto end;
}
- else if (!payload)
+ else if (!(header = malloc(sz)))
{
- fprintf(stderr, "%s: get_payload failed\n", __func__);
+ fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
+ goto end;
+ }
+ else if (!sodium_bin2base64(header, sz, (const unsigned char *)jwt_header,
+ hlen, VARIANT))
+ {
+ fprintf(stderr, "%s: sodium_bin2base64 failed\n", __func__);
goto end;
}
else if (dynstr_append(&jwt, "%s.%s", header, payload))
@@ -111,17 +140,17 @@ end:
return ret;
}
-int jwt_check(const char *const jwt, const void *const key, const size_t n)
+int jwt_check(const char *const jwt, const void *const key,
+ const size_t n)
{
const char *const p = strrchr(jwt, '.');
- const EVP_MD *const md = EVP_sha256();
- unsigned char hmac[SHA256_DIGEST_LENGTH];
- char *dhmac = NULL;
- size_t hmaclen;
+ const unsigned char *const in = (const unsigned char *)jwt;
+ unsigned char hmac[crypto_auth_hmacsha256_KEYBYTES];
- if (!md)
+ if (n != crypto_auth_hmacsha256_KEYBYTES)
{
- fprintf(stderr, "%s: EVP_sha256 failed\n", __func__);
+ fprintf(stderr, "%s: invalid key size (%zu), expected %u\n", __func__,
+ n, crypto_auth_hmacsha256_KEYBYTES);
return -1;
}
else if (!p)
@@ -129,19 +158,14 @@ int jwt_check(const char *const jwt, const void *const key, const size_t n)
fprintf(stderr, "%s: expected '.'\n", __func__);
return 1;
}
- else if (!HMAC(md, key, n, (const unsigned char *)jwt, p - jwt, hmac, NULL))
+ else if (sodium_base642bin(hmac, sizeof hmac, p + 1, strlen(p + 1),
+ NULL, NULL, NULL, VARIANT))
{
- fprintf(stderr, "%s: HMAC failed\n", __func__);
- return -1;
- }
- else if (!(dhmac = base64_decode(p + 1, &hmaclen)))
- {
- fprintf(stderr, "%s: base64_decode failed\n", __func__);
+ fprintf(stderr, "%s: sodium_base642bin failed\n", __func__);
return 1;
}
+ else if (crypto_auth_hmacsha256_verify(hmac, in, p - jwt, key))
+ return 1;
- const int r = memcmp(dhmac, hmac, hmaclen);
-
- free(dhmac);
- return !!r;
+ return 0;
}