mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

SHA-1 can be written in fewer than 100 lines of code. Just add a basic SHA-1 implementation so that there's no need to use an external library or try to pull in the kernel's SHA-1 implementation. The kernel's SHA-1 implementation is not really intended to be pulled into userspace programs in the way that it was proposed to do so for perf (https://lore.kernel.org/r/20250521225307.743726-3-yuzhuo@google.com/), and it's also likely to undergo some refactoring in the future. There's no need to tie userspace tools to it. Include a test for sha1() in the util test suite. Signed-off-by: Eric Biggers <ebiggers@kernel.org> Reviewed-by: Ian Rogers <irogers@google.com> Link: https://lore.kernel.org/r/20250625202311.23244-3-ebiggers@kernel.org Signed-off-by: Namhyung Kim <namhyung@kernel.org>
97 lines
2.9 KiB
C
97 lines
2.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* SHA-1 message digest algorithm
|
|
*
|
|
* Copyright 2025 Google LLC
|
|
*/
|
|
#include <linux/bitops.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/unaligned.h>
|
|
#include <string.h>
|
|
|
|
#include "sha1.h"
|
|
|
|
#define SHA1_BLOCK_SIZE 64
|
|
|
|
static const u32 sha1_K[4] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 };
|
|
|
|
#define SHA1_ROUND(i, a, b, c, d, e) \
|
|
do { \
|
|
if ((i) >= 16) \
|
|
w[i] = rol32(w[(i) - 16] ^ w[(i) - 14] ^ w[(i) - 8] ^ \
|
|
w[(i) - 3], \
|
|
1); \
|
|
e += w[i] + rol32(a, 5) + sha1_K[(i) / 20]; \
|
|
if ((i) < 20) \
|
|
e += (b & (c ^ d)) ^ d; \
|
|
else if ((i) < 40 || (i) >= 60) \
|
|
e += b ^ c ^ d; \
|
|
else \
|
|
e += (c & d) ^ (b & (c ^ d)); \
|
|
b = rol32(b, 30); \
|
|
/* The new (a, b, c, d, e) is the old (e, a, b, c, d). */ \
|
|
} while (0)
|
|
|
|
#define SHA1_5ROUNDS(i) \
|
|
do { \
|
|
SHA1_ROUND((i) + 0, a, b, c, d, e); \
|
|
SHA1_ROUND((i) + 1, e, a, b, c, d); \
|
|
SHA1_ROUND((i) + 2, d, e, a, b, c); \
|
|
SHA1_ROUND((i) + 3, c, d, e, a, b); \
|
|
SHA1_ROUND((i) + 4, b, c, d, e, a); \
|
|
} while (0)
|
|
|
|
#define SHA1_20ROUNDS(i) \
|
|
do { \
|
|
SHA1_5ROUNDS((i) + 0); \
|
|
SHA1_5ROUNDS((i) + 5); \
|
|
SHA1_5ROUNDS((i) + 10); \
|
|
SHA1_5ROUNDS((i) + 15); \
|
|
} while (0)
|
|
|
|
static void sha1_blocks(u32 h[5], const u8 *data, size_t nblocks)
|
|
{
|
|
while (nblocks--) {
|
|
u32 a = h[0];
|
|
u32 b = h[1];
|
|
u32 c = h[2];
|
|
u32 d = h[3];
|
|
u32 e = h[4];
|
|
u32 w[80];
|
|
|
|
for (int i = 0; i < 16; i++)
|
|
w[i] = get_unaligned_be32(&data[i * 4]);
|
|
SHA1_20ROUNDS(0);
|
|
SHA1_20ROUNDS(20);
|
|
SHA1_20ROUNDS(40);
|
|
SHA1_20ROUNDS(60);
|
|
|
|
h[0] += a;
|
|
h[1] += b;
|
|
h[2] += c;
|
|
h[3] += d;
|
|
h[4] += e;
|
|
data += SHA1_BLOCK_SIZE;
|
|
}
|
|
}
|
|
|
|
/* Calculate the SHA-1 message digest of the given data. */
|
|
void sha1(const void *data, size_t len, u8 out[SHA1_DIGEST_SIZE])
|
|
{
|
|
u32 h[5] = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476,
|
|
0xC3D2E1F0 };
|
|
u8 final_data[2 * SHA1_BLOCK_SIZE] = { 0 };
|
|
size_t final_len = len % SHA1_BLOCK_SIZE;
|
|
|
|
sha1_blocks(h, data, len / SHA1_BLOCK_SIZE);
|
|
|
|
memcpy(final_data, data + len - final_len, final_len);
|
|
final_data[final_len] = 0x80;
|
|
final_len = round_up(final_len + 9, SHA1_BLOCK_SIZE);
|
|
put_unaligned_be64((u64)len * 8, &final_data[final_len - 8]);
|
|
|
|
sha1_blocks(h, final_data, final_len / SHA1_BLOCK_SIZE);
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
put_unaligned_be32(h[i], &out[i * 4]);
|
|
}
|