| // 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]); |
| } |