UltrafastSecp256k1 3.68.0
Ultra high-performance secp256k1 elliptic curve cryptography library
Loading...
Searching...
No Matches
sha256.hpp
Go to the documentation of this file.
1#ifndef SECP256K1_SHA256_HPP
2#define SECP256K1_SHA256_HPP
3#pragma once
4
5// ============================================================================
6// SHA-256 implementation for ECDSA / Schnorr
7// ============================================================================
8// Hardware-accelerated via SHA-NI when available (runtime dispatch).
9// Falls back to portable C++ on non-x86 or CPUs without SHA extensions.
10// ============================================================================
11
12#include <array>
13#include <cstdint>
14#include <cstddef>
15#include <cstring>
16
17namespace secp256k1 {
18
19// Forward declare: implemented in hash_accel.cpp, dispatches to SHA-NI or scalar
20namespace detail {
21#if defined(__GNUC__) || defined(__clang__)
22 __attribute__((hot))
23#endif
24 void sha256_compress_dispatch(const std::uint8_t block[64],
25 std::uint32_t state[8]) noexcept;
26}
27
28class SHA256 {
29public:
30 using digest_type = std::array<std::uint8_t, 32>;
31
32 SHA256() noexcept { reset(); }
33
34 void reset() noexcept {
35 state_[0] = 0x6a09e667u; state_[1] = 0xbb67ae85u;
36 state_[2] = 0x3c6ef372u; state_[3] = 0xa54ff53au;
37 state_[4] = 0x510e527fu; state_[5] = 0x9b05688cu;
38 state_[6] = 0x1f83d9abu; state_[7] = 0x5be0cd19u;
39 total_ = 0;
40 buf_len_ = 0;
41 }
42
43#if defined(__GNUC__) && !defined(__clang__)
44#pragma GCC diagnostic push
45#pragma GCC diagnostic ignored "-Wstringop-overflow"
46#endif
47 void update(const void* data, std::size_t len) noexcept {
48 auto ptr = static_cast<const std::uint8_t*>(data);
49 total_ += len;
50
51 if (buf_len_ > 0) {
52 std::size_t const fill = 64 - buf_len_;
53 if (len < fill) {
54#if defined(__GNUC__) && !defined(__clang__)
55#pragma GCC diagnostic push
56#pragma GCC diagnostic ignored "-Wstringop-overflow"
57#endif
58 std::memcpy(buf_ + buf_len_, ptr, len); // len < fill <= 64, safe
59#if defined(__GNUC__) && !defined(__clang__)
60#pragma GCC diagnostic pop
61#endif
62 buf_len_ += len;
63 return;
64 }
65#if defined(__GNUC__) && !defined(__clang__)
66#pragma GCC diagnostic push
67#pragma GCC diagnostic ignored "-Wstringop-overflow"
68#endif
69 std::memcpy(buf_ + buf_len_, ptr, fill); // fill = 64 - buf_len_, safe
70#if defined(__GNUC__) && !defined(__clang__)
71#pragma GCC diagnostic pop
72#endif
74 ptr += fill;
75 len -= fill;
76 buf_len_ = 0;
77 }
78
79 while (len >= 64) {
81 ptr += 64;
82 len -= 64;
83 }
84
85 if (len > 0) {
86 std::memcpy(buf_, ptr, len);
87 buf_len_ = len;
88 }
89 }
90#if defined(__GNUC__) && !defined(__clang__)
91#pragma GCC diagnostic pop
92#endif
93
94 digest_type finalize() noexcept {
95 std::uint64_t const bits = total_ * 8;
96
97 // -- Direct in-place padding (no per-byte update() calls) ---------
98 // buf_len_ is invariantly [0,63] after update() processes full blocks.
99 // Explicit bounds check satisfies static analysis (Sonar cpp:S3519).
100 if (buf_len_ >= 64) buf_len_ = 0;
101 std::size_t const pos = buf_len_; // capture index before increment
102 buf_len_ = pos + 1; // new length [1, 64]
103 buf_[pos] = 0x80; // write at [0, 63] -- always in bounds
104
105 if (buf_len_ > 56) {
106 // No room for 8-byte length -- pad, compress, start fresh block
107 // buf_len_ is [57, 64]; (64 - buf_len_) is [0, 7]
108 if (buf_len_ < 64) {
109 std::memset(buf_ + buf_len_, 0, 64 - buf_len_);
110 }
112 buf_len_ = 0;
113 }
114
115 // Zero-pad to byte 56
116 std::memset(buf_ + buf_len_, 0, 56 - buf_len_);
117
118 // Append bit-length big-endian at bytes 56..63
119 buf_[56] = static_cast<std::uint8_t>(bits >> 56);
120 buf_[57] = static_cast<std::uint8_t>(bits >> 48);
121 buf_[58] = static_cast<std::uint8_t>(bits >> 40);
122 buf_[59] = static_cast<std::uint8_t>(bits >> 32);
123 buf_[60] = static_cast<std::uint8_t>(bits >> 24);
124 buf_[61] = static_cast<std::uint8_t>(bits >> 16);
125 buf_[62] = static_cast<std::uint8_t>(bits >> 8);
126 buf_[63] = static_cast<std::uint8_t>(bits);
127
129
130 digest_type out{};
131 for (std::size_t i = 0; i < 8; ++i) {
132 out[i * 4 + 0] = static_cast<std::uint8_t>(state_[i] >> 24);
133 out[i * 4 + 1] = static_cast<std::uint8_t>(state_[i] >> 16);
134 out[i * 4 + 2] = static_cast<std::uint8_t>(state_[i] >> 8);
135 out[i * 4 + 3] = static_cast<std::uint8_t>(state_[i]);
136 }
137 return out;
138 }
139
140 // One-shot convenience
141 static digest_type hash(const void* data, std::size_t len) noexcept {
142 SHA256 ctx;
143 ctx.update(data, len);
144 return ctx.finalize();
145 }
146
147 // Double-SHA256: SHA256(SHA256(data))
148 static digest_type hash256(const void* data, std::size_t len) noexcept {
149 auto h1 = hash(data, len);
150 return hash(h1.data(), h1.size());
151 }
152
153private:
154 std::uint32_t state_[8]{};
155 std::uint8_t buf_[64]{};
156 std::size_t buf_len_ = 0;
157 std::uint64_t total_ = 0;
158};
159
160} // namespace secp256k1
161
162#endif // SECP256K1_SHA256_HPP
static digest_type hash(const void *data, std::size_t len) noexcept
Definition sha256.hpp:141
void reset() noexcept
Definition sha256.hpp:34
static digest_type hash256(const void *data, std::size_t len) noexcept
Definition sha256.hpp:148
SHA256() noexcept
Definition sha256.hpp:32
digest_type finalize() noexcept
Definition sha256.hpp:94
void update(const void *data, std::size_t len) noexcept
Definition sha256.hpp:47
std::array< std::uint8_t, 32 > digest_type
Definition sha256.hpp:30
void sha256_compress_dispatch(const std::uint8_t block[64], std::uint32_t state[8]) noexcept