ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
sha3.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#include "crypto/hash/sha3.h"
5
6#if PC_ENABLE_PQC_KEX
7
8// Keccak-f[1600] permutation constants (FIPS 202): iota round constants, rho rotation offsets, and
9// the rho/pi lane-permutation order.
10static const uint64_t pc_keccak_rc[24] = {
11 0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, 0x000000000000808bULL,
12 0x0000000080000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL, 0x0000000000000088ULL,
13 0x0000000080008009ULL, 0x000000008000000aULL, 0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL,
14 0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL, 0x000000000000800aULL, 0x800000008000000aULL,
15 0x8000000080008081ULL, 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL};
16
17static const uint8_t pc_keccak_rot[24] = {1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
18 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44};
19
20static const uint8_t pc_keccak_pi[24] = {10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
21 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1};
22
23static inline uint64_t rotl64(uint64_t x, unsigned n)
24{
25 return (x << n) | (x >> (64 - n));
26}
27
28static void keccakf(uint64_t st[25])
29{
30 for (int r = 0; r < 24; r++)
31 {
32 uint64_t bc[5];
33 // Theta
34 for (int i = 0; i < 5; i++)
35 {
36 bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20];
37 }
38 for (int i = 0; i < 5; i++)
39 {
40 uint64_t t = bc[(i + 4) % 5] ^ rotl64(bc[(i + 1) % 5], 1);
41 for (int j = 0; j < 25; j += 5)
42 {
43 st[j + i] ^= t;
44 }
45 }
46 // Rho + Pi
47 uint64_t t = st[1];
48 for (int i = 0; i < 24; i++)
49 {
50 int j = pc_keccak_pi[i];
51 uint64_t tmp = st[j];
52 st[j] = rotl64(t, pc_keccak_rot[i]);
53 t = tmp;
54 }
55 // Chi
56 for (int j = 0; j < 25; j += 5)
57 {
58 for (int i = 0; i < 5; i++)
59 {
60 bc[i] = st[j + i];
61 }
62 for (int i = 0; i < 5; i++)
63 {
64 st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5];
65 }
66 }
67 // Iota
68 st[0] ^= pc_keccak_rc[r];
69 }
70}
71
72// The sponge state is addressed as a little-endian octet string: octet p lives in lane p/8 at byte
73// p%8. This makes absorb/squeeze byte order independent of the host's.
74static inline void st_xor_byte(uint64_t st[25], size_t p, uint8_t b)
75{
76 st[p >> 3] ^= (uint64_t)b << (8 * (p & 7));
77}
78
79static inline uint8_t st_get_byte(const uint64_t st[25], size_t p)
80{
81 return (uint8_t)(st[p >> 3] >> (8 * (p & 7)));
82}
83
84void pc_keccak_absorb(KeccakCtx *c, uint32_t rate, const uint8_t *in, size_t inlen, uint8_t domain)
85{
86 for (int i = 0; i < 25; i++)
87 {
88 c->st[i] = 0;
89 }
90 c->rate = rate;
91
92 while (inlen >= rate)
93 {
94 for (uint32_t p = 0; p < rate; p++)
95 {
96 st_xor_byte(c->st, p, in[p]);
97 }
98 keccakf(c->st);
99 in += rate;
100 inlen -= rate;
101 }
102 for (size_t p = 0; p < inlen; p++)
103 {
104 st_xor_byte(c->st, p, in[p]);
105 }
106 // Pad10*1 with the domain-separation byte, and the high bit of the last rate octet.
107 st_xor_byte(c->st, inlen, domain);
108 st_xor_byte(c->st, rate - 1, 0x80);
109 c->out_pos = rate; // force a permutation on the first squeeze
110}
111
112void pc_keccak_squeeze(KeccakCtx *c, uint8_t *out, size_t outlen)
113{
114 while (outlen)
115 {
116 if (c->out_pos == c->rate)
117 {
118 keccakf(c->st);
119 c->out_pos = 0;
120 }
121 uint32_t n = c->rate - c->out_pos;
122 if (n > outlen)
123 {
124 n = (uint32_t)outlen;
125 }
126 for (uint32_t k = 0; k < n; k++)
127 {
128 out[k] = st_get_byte(c->st, c->out_pos + k);
129 }
130 out += n;
131 outlen -= n;
132 c->out_pos += n;
133 }
134}
135
136void sha3_256(uint8_t out[32], const uint8_t *in, size_t inlen)
137{
138 KeccakCtx c;
139 pc_keccak_absorb(&c, KECCAK_RATE_SHA3_256, in, inlen, 0x06);
140 pc_keccak_squeeze(&c, out, 32);
141}
142
143void sha3_512(uint8_t out[64], const uint8_t *in, size_t inlen)
144{
145 KeccakCtx c;
146 pc_keccak_absorb(&c, KECCAK_RATE_SHA3_512, in, inlen, 0x06);
147 pc_keccak_squeeze(&c, out, 64);
148}
149
150void shake128(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen)
151{
152 KeccakCtx c;
153 pc_keccak_absorb(&c, KECCAK_RATE_SHAKE128, in, inlen, 0x1F);
154 pc_keccak_squeeze(&c, out, outlen);
155}
156
157void shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen)
158{
159 KeccakCtx c;
160 pc_keccak_absorb(&c, KECCAK_RATE_SHAKE256, in, inlen, 0x1F);
161 pc_keccak_squeeze(&c, out, outlen);
162}
163
164void pc_shake128_absorb(KeccakCtx *c, const uint8_t *in, size_t inlen)
165{
166 pc_keccak_absorb(c, KECCAK_RATE_SHAKE128, in, inlen, 0x1F);
167}
168
169#endif // PC_ENABLE_PQC_KEX
Keccak-f[1600] sponge: SHA3-256, SHA3-512, SHAKE128, SHAKE256 (FIPS 202).