ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
sha1.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file sha1.cpp
6 * @brief SHA-1 implementation (FIPS 180-4).
7 *
8 * On Arduino (ESP32) targets, delegates to mbedtls_sha1() which uses the hardware SHA accelerator. On
9 * native (x86) test targets, uses a portable software implementation so unit tests run without mbedTLS.
10 */
11
12#include "crypto/hash/sha1.h"
13#include "crypto/crypto_opt.h"
14#include <string.h>
15#ifdef ARDUINO
16#include "mbedtls/sha1.h" // hardware-accelerated SHA-1 on ESP32
17#else
18#include "shared_primitives/endian.h" // native software SHA-1
19#endif
21
22#ifdef ARDUINO
23
24// --- ESP32 / Arduino: use mbedTLS (hardware-accelerated on ESP32) ----------
25
26void pc_sha1(const uint8_t *data, size_t len, uint8_t digest[PC_SHA1_DIGEST_LEN])
27{
28 (void)mbedtls_sha1(data, len, digest);
29}
30
31#else
32
33// --- Native / test: software SHA-1, no external dependencies ---------------
34
35static inline uint32_t rot32(uint32_t x, int n)
36{
37 return (x << n) | (x >> (32 - n));
38}
39
40// Process one 64-byte block into the running state h[0..4] (FIPS 180-4 ยง6.1.2).
41static void sha1_block(uint32_t h[5], const uint8_t block[64])
42{
43 // Message schedule: 16 big-endian words from the block, extended to 80 by XOR-and-rotate (the
44 // SHA-1 recurrence; the rotate-by-1 is what SHA-0 lacked).
45 uint32_t w[80];
46 for (int i = 0; i < 16; i++)
47 {
48 w[i] = pc_rd32be(block + i * 4);
49 }
50 for (int i = 16; i < 80; i++)
51 {
52 w[i] = rot32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
53 }
54
55 uint32_t a = h[0];
56 uint32_t b = h[1];
57 uint32_t c = h[2];
58 uint32_t d = h[3];
59 uint32_t e = h[4];
60
61 // 80 rounds in four 20-round regimes, each with its own mixing function f and constant k.
62 for (int i = 0; i < 80; i++)
63 {
64 uint32_t f;
65 uint32_t k;
66 if (i < 20)
67 {
68 f = (b & c) | (~b & d); // choice
69 k = 0x5A827999u;
70 }
71 else if (i < 40)
72 {
73 f = b ^ c ^ d; // parity
74 k = 0x6ED9EBA1u;
75 }
76 else if (i < 60)
77 {
78 f = (b & c) | (b & d) | (c & d); // majority
79 k = 0x8F1BBCDCu;
80 }
81 else
82 {
83 f = b ^ c ^ d; // parity
84 k = 0xCA62C1D6u;
85 }
86
87 // Round update; b is rotated 30 as it shifts into c.
88 uint32_t tmp = rot32(a, 5) + f + e + k + w[i];
89 e = d;
90 d = c;
91 c = rot32(b, 30);
92 b = a;
93 a = tmp;
94 }
95
96 h[0] += a;
97 h[1] += b;
98 h[2] += c;
99 h[3] += d;
100 h[4] += e;
101}
102
103void pc_sha1(const uint8_t *data, size_t len, uint8_t digest[PC_SHA1_DIGEST_LEN])
104{
105 uint32_t h[5] = {0x67452301u, 0xEFCDAB89u, 0x98BADCFEu, 0x10325476u, 0xC3D2E1F0u};
106
107 // Process full 64-byte blocks
108 size_t blocks = len / 64;
109 for (size_t i = 0; i < blocks; i++)
110 {
111 sha1_block(h, data + i * 64);
112 }
113
114 // Build the padded final block(s)
115 uint8_t pad[128] = {};
116 size_t tail = len - blocks * 64;
117 memcpy(pad, data + blocks * 64, tail);
118 pad[tail] = 0x80;
119
120 // Bit-length goes in the last 8 bytes of the final block
121 uint64_t bit_len = (uint64_t)len * 8;
122 uint8_t *bl = (tail < 56) ? pad + 56 : pad + 120;
123 for (int i = 7; i >= 0; i--, bit_len >>= 8)
124 {
125 bl[i] = (uint8_t)bit_len;
126 }
127
128 sha1_block(h, pad);
129 if (tail >= 56)
130 {
131 sha1_block(h, pad + 64);
132 }
133
134 for (int i = 0; i < 5; i++)
135 {
136 pc_wr32be(digest + i * 4, h[i]);
137 }
138}
139
140#endif // ARDUINO
Per-translation-unit optimization override for hot, pure-integer crypto.
Fixed-width integer serializers into a raw uint8_t* buffer - one source of truth.
uint32_t pc_rd32be(const uint8_t *p)
Read a big-endian u32 at p.
Definition endian.h:119
size_t pc_wr32be(uint8_t *p, uint32_t v)
Write v big-endian at p.
Definition endian.h:93
PC_CRYPTO_HOT void pc_sha1(const uint8_t *data, size_t len, uint8_t digest[PC_SHA1_DIGEST_LEN])
Compute a SHA-1 digest over an arbitrary byte buffer.
Definition sha1.cpp:26
SHA-1 (FIPS 180-4) - one-shot digest.
#define PC_SHA1_DIGEST_LEN
SHA-1 digest length in bytes.
Definition sha1.h:23