Skip to content

Commit a47e596

Browse files
committed
Merge #19841: Implement Keccak and SHA3_256
ab654c7 Unroll Keccak-f implementation (Pieter Wuille) 3f01ddb Add SHA3 benchmark (Pieter Wuille) 2ac8bf9 Implement keccak-f[1600] and SHA3-256 (Pieter Wuille) Pull request description: Add a simple (and initially unoptimized) Keccak/SHA3 implementation based on https://github.com/mjosaarinen/tiny_sha3/blob/master/sha3.c, as one will be needed for TORv3 support (the conversion from BIP155 encoding to .onion notation uses a SHA3-based checksum). In follow-up commits, a benchmark is added, and the Keccakf function is unrolled for a (for me) 4.9x speedup. Test vectors are taken from https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/secure-hashing#sha3vsha3vss. ACKs for top commit: practicalswift: ACK ab654c7 -- patch looks correct and no sanitizer complaints when doing some basic fuzz testing of the added code (remember: **don't trust: fuzz!**) :) laanwj: re-ACK ab654c7 vasild: ACK ab654c7 Tree-SHA512: 8a91b18c46e8fb178b7ff82046cff626180362337e515b92fbbd771876e795da2ed4e3995eb4849773040287f6e687237f469a90474ac53f521fc12e0f5031d9
2 parents 564e1ab + ab654c7 commit a47e596

File tree

5 files changed

+322
-0
lines changed

5 files changed

+322
-0
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,8 @@ crypto_libbitcoin_crypto_base_a_SOURCES = \
403403
crypto/sha1.h \
404404
crypto/sha256.cpp \
405405
crypto/sha256.h \
406+
crypto/sha3.cpp \
407+
crypto/sha3.h \
406408
crypto/sha512.cpp \
407409
crypto/sha512.h \
408410
crypto/siphash.cpp \

src/bench/crypto_hash.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <crypto/ripemd160.h>
88
#include <crypto/sha1.h>
99
#include <crypto/sha256.h>
10+
#include <crypto/sha3.h>
1011
#include <crypto/sha512.h>
1112
#include <crypto/siphash.h>
1213
#include <hash.h>
@@ -43,6 +44,15 @@ static void SHA256(benchmark::Bench& bench)
4344
});
4445
}
4546

47+
static void SHA3_256_1M(benchmark::Bench& bench)
48+
{
49+
uint8_t hash[SHA3_256::OUTPUT_SIZE];
50+
std::vector<uint8_t> in(BUFFER_SIZE,0);
51+
bench.batch(in.size()).unit("byte").run([&] {
52+
SHA3_256().Write(in).Finalize(hash);
53+
});
54+
}
55+
4656
static void SHA256_32b(benchmark::Bench& bench)
4757
{
4858
std::vector<uint8_t> in(32,0);
@@ -99,6 +109,7 @@ BENCHMARK(RIPEMD160);
99109
BENCHMARK(SHA1);
100110
BENCHMARK(SHA256);
101111
BENCHMARK(SHA512);
112+
BENCHMARK(SHA3_256_1M);
102113

103114
BENCHMARK(SHA256_32b);
104115
BENCHMARK(SipHash_32b);

src/crypto/sha3.cpp

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// Copyright (c) 2020 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
// Based on https://github.com/mjosaarinen/tiny_sha3/blob/master/sha3.c
6+
// by Markku-Juhani O. Saarinen <[email protected]>
7+
8+
#include <crypto/sha3.h>
9+
#include <crypto/common.h>
10+
#include <span.h>
11+
12+
#include <algorithm>
13+
#include <array> // For std::begin and std::end.
14+
15+
#include <stdint.h>
16+
17+
// Internal implementation code.
18+
namespace
19+
{
20+
uint64_t Rotl(uint64_t x, int n) { return (x << n) | (x >> (64 - n)); }
21+
} // namespace
22+
23+
void KeccakF(uint64_t (&st)[25])
24+
{
25+
static constexpr uint64_t RNDC[24] = {
26+
0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
27+
0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
28+
0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
29+
0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
30+
0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
31+
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
32+
};
33+
static constexpr int ROUNDS = 24;
34+
35+
for (int round = 0; round < ROUNDS; ++round) {
36+
uint64_t bc0, bc1, bc2, bc3, bc4, t;
37+
38+
// Theta
39+
bc0 = st[0] ^ st[5] ^ st[10] ^ st[15] ^ st[20];
40+
bc1 = st[1] ^ st[6] ^ st[11] ^ st[16] ^ st[21];
41+
bc2 = st[2] ^ st[7] ^ st[12] ^ st[17] ^ st[22];
42+
bc3 = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23];
43+
bc4 = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24];
44+
t = bc4 ^ Rotl(bc1, 1); st[0] ^= t; st[5] ^= t; st[10] ^= t; st[15] ^= t; st[20] ^= t;
45+
t = bc0 ^ Rotl(bc2, 1); st[1] ^= t; st[6] ^= t; st[11] ^= t; st[16] ^= t; st[21] ^= t;
46+
t = bc1 ^ Rotl(bc3, 1); st[2] ^= t; st[7] ^= t; st[12] ^= t; st[17] ^= t; st[22] ^= t;
47+
t = bc2 ^ Rotl(bc4, 1); st[3] ^= t; st[8] ^= t; st[13] ^= t; st[18] ^= t; st[23] ^= t;
48+
t = bc3 ^ Rotl(bc0, 1); st[4] ^= t; st[9] ^= t; st[14] ^= t; st[19] ^= t; st[24] ^= t;
49+
50+
// Rho Pi
51+
t = st[1];
52+
bc0 = st[10]; st[10] = Rotl(t, 1); t = bc0;
53+
bc0 = st[7]; st[7] = Rotl(t, 3); t = bc0;
54+
bc0 = st[11]; st[11] = Rotl(t, 6); t = bc0;
55+
bc0 = st[17]; st[17] = Rotl(t, 10); t = bc0;
56+
bc0 = st[18]; st[18] = Rotl(t, 15); t = bc0;
57+
bc0 = st[3]; st[3] = Rotl(t, 21); t = bc0;
58+
bc0 = st[5]; st[5] = Rotl(t, 28); t = bc0;
59+
bc0 = st[16]; st[16] = Rotl(t, 36); t = bc0;
60+
bc0 = st[8]; st[8] = Rotl(t, 45); t = bc0;
61+
bc0 = st[21]; st[21] = Rotl(t, 55); t = bc0;
62+
bc0 = st[24]; st[24] = Rotl(t, 2); t = bc0;
63+
bc0 = st[4]; st[4] = Rotl(t, 14); t = bc0;
64+
bc0 = st[15]; st[15] = Rotl(t, 27); t = bc0;
65+
bc0 = st[23]; st[23] = Rotl(t, 41); t = bc0;
66+
bc0 = st[19]; st[19] = Rotl(t, 56); t = bc0;
67+
bc0 = st[13]; st[13] = Rotl(t, 8); t = bc0;
68+
bc0 = st[12]; st[12] = Rotl(t, 25); t = bc0;
69+
bc0 = st[2]; st[2] = Rotl(t, 43); t = bc0;
70+
bc0 = st[20]; st[20] = Rotl(t, 62); t = bc0;
71+
bc0 = st[14]; st[14] = Rotl(t, 18); t = bc0;
72+
bc0 = st[22]; st[22] = Rotl(t, 39); t = bc0;
73+
bc0 = st[9]; st[9] = Rotl(t, 61); t = bc0;
74+
bc0 = st[6]; st[6] = Rotl(t, 20); t = bc0;
75+
st[1] = Rotl(t, 44);
76+
77+
// Chi Iota
78+
bc0 = st[0]; bc1 = st[1]; bc2 = st[2]; bc3 = st[3]; bc4 = st[4];
79+
st[0] = bc0 ^ (~bc1 & bc2) ^ RNDC[round];
80+
st[1] = bc1 ^ (~bc2 & bc3);
81+
st[2] = bc2 ^ (~bc3 & bc4);
82+
st[3] = bc3 ^ (~bc4 & bc0);
83+
st[4] = bc4 ^ (~bc0 & bc1);
84+
bc0 = st[5]; bc1 = st[6]; bc2 = st[7]; bc3 = st[8]; bc4 = st[9];
85+
st[5] = bc0 ^ (~bc1 & bc2);
86+
st[6] = bc1 ^ (~bc2 & bc3);
87+
st[7] = bc2 ^ (~bc3 & bc4);
88+
st[8] = bc3 ^ (~bc4 & bc0);
89+
st[9] = bc4 ^ (~bc0 & bc1);
90+
bc0 = st[10]; bc1 = st[11]; bc2 = st[12]; bc3 = st[13]; bc4 = st[14];
91+
st[10] = bc0 ^ (~bc1 & bc2);
92+
st[11] = bc1 ^ (~bc2 & bc3);
93+
st[12] = bc2 ^ (~bc3 & bc4);
94+
st[13] = bc3 ^ (~bc4 & bc0);
95+
st[14] = bc4 ^ (~bc0 & bc1);
96+
bc0 = st[15]; bc1 = st[16]; bc2 = st[17]; bc3 = st[18]; bc4 = st[19];
97+
st[15] = bc0 ^ (~bc1 & bc2);
98+
st[16] = bc1 ^ (~bc2 & bc3);
99+
st[17] = bc2 ^ (~bc3 & bc4);
100+
st[18] = bc3 ^ (~bc4 & bc0);
101+
st[19] = bc4 ^ (~bc0 & bc1);
102+
bc0 = st[20]; bc1 = st[21]; bc2 = st[22]; bc3 = st[23]; bc4 = st[24];
103+
st[20] = bc0 ^ (~bc1 & bc2);
104+
st[21] = bc1 ^ (~bc2 & bc3);
105+
st[22] = bc2 ^ (~bc3 & bc4);
106+
st[23] = bc3 ^ (~bc4 & bc0);
107+
st[24] = bc4 ^ (~bc0 & bc1);
108+
}
109+
}
110+
111+
SHA3_256& SHA3_256::Write(Span<const unsigned char> data)
112+
{
113+
if (m_bufsize && m_bufsize + data.size() >= sizeof(m_buffer)) {
114+
// Fill the buffer and process it.
115+
std::copy(data.begin(), data.begin() + sizeof(m_buffer) - m_bufsize, m_buffer + m_bufsize);
116+
data = data.subspan(sizeof(m_buffer) - m_bufsize);
117+
m_state[m_pos++] ^= ReadLE64(m_buffer);
118+
m_bufsize = 0;
119+
if (m_pos == RATE_BUFFERS) {
120+
KeccakF(m_state);
121+
m_pos = 0;
122+
}
123+
}
124+
while (data.size() >= sizeof(m_buffer)) {
125+
// Process chunks directly from the buffer.
126+
m_state[m_pos++] ^= ReadLE64(data.data());
127+
data = data.subspan(8);
128+
if (m_pos == RATE_BUFFERS) {
129+
KeccakF(m_state);
130+
m_pos = 0;
131+
}
132+
}
133+
if (data.size()) {
134+
// Keep the remainder in the buffer.
135+
std::copy(data.begin(), data.end(), m_buffer + m_bufsize);
136+
m_bufsize += data.size();
137+
}
138+
return *this;
139+
}
140+
141+
SHA3_256& SHA3_256::Finalize(Span<unsigned char> output)
142+
{
143+
assert(output.size() == OUTPUT_SIZE);
144+
std::fill(m_buffer + m_bufsize, m_buffer + sizeof(m_buffer), 0);
145+
m_buffer[m_bufsize] ^= 0x06;
146+
m_state[m_pos] ^= ReadLE64(m_buffer);
147+
m_state[RATE_BUFFERS - 1] ^= 0x8000000000000000;
148+
KeccakF(m_state);
149+
for (unsigned i = 0; i < 4; ++i) {
150+
WriteLE64(output.data() + 8 * i, m_state[i]);
151+
}
152+
return *this;
153+
}
154+
155+
SHA3_256& SHA3_256::Reset()
156+
{
157+
m_bufsize = 0;
158+
m_pos = 0;
159+
std::fill(std::begin(m_state), std::end(m_state), 0);
160+
return *this;
161+
}

src/crypto/sha3.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) 2020 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_CRYPTO_SHA3_H
6+
#define BITCOIN_CRYPTO_SHA3_H
7+
8+
#include <span.h>
9+
10+
#include <stdint.h>
11+
#include <stdlib.h>
12+
13+
//! The Keccak-f[1600] transform.
14+
void KeccakF(uint64_t (&st)[25]);
15+
16+
class SHA3_256
17+
{
18+
private:
19+
uint64_t m_state[25] = {0};
20+
unsigned char m_buffer[8];
21+
unsigned m_bufsize = 0;
22+
unsigned m_pos = 0;
23+
24+
//! Sponge rate in bits.
25+
static constexpr unsigned RATE_BITS = 1088;
26+
27+
//! Sponge rate expressed as a multiple of the buffer size.
28+
static constexpr unsigned RATE_BUFFERS = RATE_BITS / (8 * sizeof(m_buffer));
29+
30+
static_assert(RATE_BITS % (8 * sizeof(m_buffer)) == 0, "Rate must be a multiple of 8 bytes");
31+
32+
public:
33+
static constexpr size_t OUTPUT_SIZE = 32;
34+
35+
SHA3_256() {}
36+
SHA3_256& Write(Span<const unsigned char> data);
37+
SHA3_256& Finalize(Span<unsigned char> output);
38+
SHA3_256& Reset();
39+
};
40+
41+
#endif // BITCOIN_CRYPTO_SHA3_H

0 commit comments

Comments
 (0)