]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
basic: add hmac_sha256 implementation
authorLuca Boccassi <luca.boccassi@microsoft.com>
Thu, 7 Oct 2021 20:02:44 +0000 (21:02 +0100)
committerLuca Boccassi <luca.boccassi@microsoft.com>
Fri, 8 Oct 2021 12:11:00 +0000 (13:11 +0100)
Based on the FIPS 198 specification. Not optimized and probably
completely unsafe, to be used only for non-strong-cryptographic
purposes when OpenSSL cannot be used.

src/basic/hmac.c [new file with mode: 0644]
src/basic/hmac.h [new file with mode: 0644]
src/basic/meson.build
src/test/meson.build
src/test/test-hmac.c [new file with mode: 0644]

diff --git a/src/basic/hmac.c b/src/basic/hmac.c
new file mode 100644 (file)
index 0000000..1e4e380
--- /dev/null
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <assert.h>
+#include <string.h>
+
+#include "hmac.h"
+#include "sha256.h"
+
+#define HMAC_BLOCK_SIZE 64
+#define INNER_PADDING_BYTE 0x36
+#define OUTER_PADDING_BYTE 0x5c
+
+void hmac_sha256(const void *key,
+                 size_t key_size,
+                 const void *input,
+                 size_t input_size,
+                 uint8_t res[static SHA256_DIGEST_SIZE]) {
+
+        uint8_t inner_padding[HMAC_BLOCK_SIZE] = { };
+        uint8_t outer_padding[HMAC_BLOCK_SIZE] = { };
+        uint8_t replacement_key[SHA256_DIGEST_SIZE];
+        struct sha256_ctx hash;
+
+        assert(key);
+        assert(key_size > 0);
+        assert(res);
+
+        /* Implement algorithm as described by FIPS 198. */
+
+        /* The key needs to be block size length or less, hash it if it's longer. */
+        if (key_size > HMAC_BLOCK_SIZE) {
+                sha256_init_ctx(&hash);
+                sha256_process_bytes(key, key_size, &hash);
+                sha256_finish_ctx(&hash, replacement_key);
+                key = replacement_key;
+                key_size = SHA256_DIGEST_SIZE;
+        }
+
+        /* First, copy the key into the padding arrays. If it's shorter than
+         * the block size, the arrays are already initialized to 0. */
+        memcpy(inner_padding, key, key_size);
+        memcpy(outer_padding, key, key_size);
+
+        /* Then, XOR the provided key and any padding leftovers with the fixed
+         * padding bytes as defined in FIPS 198. */
+        for (size_t i = 0; i < HMAC_BLOCK_SIZE; i++) {
+                inner_padding[i] ^= INNER_PADDING_BYTE;
+                outer_padding[i] ^= OUTER_PADDING_BYTE;
+        }
+
+        /* First pass: hash the inner padding array and the input. */
+        sha256_init_ctx(&hash);
+        sha256_process_bytes(inner_padding, HMAC_BLOCK_SIZE, &hash);
+        sha256_process_bytes(input, input_size, &hash);
+        sha256_finish_ctx(&hash, res);
+
+        /* Second pass: hash the outer padding array and the result of the first pass. */
+        sha256_init_ctx(&hash);
+        sha256_process_bytes(outer_padding, HMAC_BLOCK_SIZE, &hash);
+        sha256_process_bytes(res, SHA256_DIGEST_SIZE, &hash);
+        sha256_finish_ctx(&hash, res);
+}
diff --git a/src/basic/hmac.h b/src/basic/hmac.h
new file mode 100644 (file)
index 0000000..12b594c
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#define SHA256_DIGEST_SIZE 32
+
+/* Unoptimized implementation based on FIPS 198. 'res' has to be allocated by
+ * the caller. Prefer external OpenSSL functions, and use this only when
+ * linking to OpenSSL is not desireable (eg: libsystemd.so). */
+void hmac_sha256(const void *key, size_t key_size, const void *input, size_t input_size, uint8_t res[static SHA256_DIGEST_SIZE]);
index adb7b666c6fcb3e3241c037fd52ba8c628b2940d..a4a7469856996f53e1ce5cdb52405701ed8fc1ab 100644 (file)
@@ -72,6 +72,8 @@ basic_sources = files('''
         hashmap.h
         hexdecoct.c
         hexdecoct.h
+        hmac.c
+        hmac.h
         hostname-util.c
         hostname-util.h
         in-addr-util.c
index fea7f107fd541d104a5344a27379b935157cc9f7..c24641fc7e8ad0c290fde9ac5ba597e4f1b4f285 100644 (file)
@@ -629,6 +629,8 @@ tests += [
 
         [['src/test/test-nscd-flush.c'],
          [], [], [], 'ENABLE_NSCD', 'manual'],
+
+        [['src/test/test-hmac.c']],
 ]
 
 ############################################################
diff --git a/src/test/test-hmac.c b/src/test/test-hmac.c
new file mode 100644 (file)
index 0000000..5c73eca
--- /dev/null
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "hexdecoct.h"
+#include "hmac.h"
+#include "string-util.h"
+#include "tests.h"
+
+static void hmac_sha256_by_string(const char *key, const char *value, uint8_t res[static SHA256_DIGEST_SIZE]) {
+        hmac_sha256(key, strlen(key), value, strlen(value), res);
+}
+
+static void test_hmac(void) {
+    uint8_t result[SHA256_DIGEST_SIZE];
+    char *hex_result = NULL;
+
+    log_info("/* %s */", __func__);
+
+    /* Results compared with output of 'echo -n "<input>" | openssl dgst -sha256 -hmac "<key>"' */
+
+    hmac_sha256_by_string("waldo",
+                          "",
+                          result);
+    hex_result = hexmem(result, sizeof(result));
+    assert_se(streq_ptr(hex_result, "cadd5e42114351181f3abff477641d88efb57d2b5641a1e5c6d623363a6d3bad"));
+    hex_result = mfree(hex_result);
+
+    hmac_sha256_by_string("waldo",
+                          "baldohaldo",
+                          result);
+    hex_result = hexmem(result, sizeof(result));
+    assert_se(streq_ptr(hex_result, "c47ad5031ba21605e52c6ca68090d66a2dd5ccf84efa4bace15361a8cba63cda"));
+    hex_result = mfree(hex_result);
+
+    hmac_sha256_by_string("waldo",
+                          "baldo haldo",
+                          result);
+    hex_result = hexmem(result, sizeof(result));
+    assert_se(streq_ptr(hex_result, "4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69"));
+    hex_result = mfree(hex_result);
+
+    hmac_sha256_by_string("waldo",
+                          "baldo 4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69 haldo",
+                          result);
+    hex_result = hexmem(result, sizeof(result));
+    assert_se(streq_ptr(hex_result, "039f3df430b19753ffb493e5b90708f75c5210b63c6bcbef3374eb3f0a3f97f7"));
+    hex_result = mfree(hex_result);
+
+    hmac_sha256_by_string("4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69",
+                          "baldo haldo",
+                          result);
+    hex_result = hexmem(result, sizeof(result));
+    assert_se(streq_ptr(hex_result, "c4cfaf48077cbb0bbd177a09e59ec4c248f4ca771503410f5b54b98d88d2f47b"));
+    hex_result = mfree(hex_result);
+
+    hmac_sha256_by_string("4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69",
+                          "supercalifragilisticexpialidocious",
+                          result);
+    hex_result = hexmem(result, sizeof(result));
+    assert_se(streq_ptr(hex_result, "2c059e7a63c4c3b23f47966a65fd2f8a2f5d7161e2e90d78ff68866b5c375cb7"));
+    hex_result = mfree(hex_result);
+
+    hmac_sha256_by_string("4e8974ad6c08b98cc2519cd1e27aa7195769fcf86db1dd7ceaab4d44c490ad69c47ad5031ba21605e52c6ca68090d66a2dd5ccf84efa4bace15361a8cba63cda",
+                          "supercalifragilisticexpialidocious",
+                          result);
+    hex_result = hexmem(result, sizeof(result));
+    assert_se(streq_ptr(hex_result, "1dd1d1d45b9d9f9673dc9983c968c46ff3168e03cfeb4156a219eba1af4cff5f"));
+    hex_result = mfree(hex_result);
+}
+
+int main(int argc, char **argv) {
+        test_setup_logging(LOG_INFO);
+
+        test_hmac();
+
+        return 0;
+}