Line data Source code
1 : /**
2 : * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 : * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 : * CANN Open Software License Agreement Version 2.0 (the "License").
5 : * Please refer to the License for details. You may not use this file except in compliance with the License.
6 : * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 : * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 : * See LICENSE in the root of the software repository for the full text of the License.
9 : */
10 : #include "tsd_sha256.h"
11 : #include <mutex>
12 : #include <securec.h>
13 : #include "common/type_def.h"
14 : #include "tsd_log.h"
15 :
16 : #if defined(__aarch64__)
17 : #include <arm_neon.h>
18 : #include <sys/auxv.h>
19 : #include <asm/hwcap.h>
20 : #define TSD_SHA256_PLATFORM_ARM 1
21 : #define TSD_SHA256_PLATFORM_X86 0
22 : #elif defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
23 : #include <immintrin.h>
24 : #define TSD_SHA256_PLATFORM_ARM 0
25 : #define TSD_SHA256_PLATFORM_X86 1
26 : #else
27 : #define TSD_SHA256_PLATFORM_ARM 0
28 : #define TSD_SHA256_PLATFORM_X86 0
29 : #endif
30 :
31 : namespace tsd {
32 : namespace sha256 {
33 :
34 : namespace {
35 :
36 : constexpr uint32_t INITIAL_HASH[8] = {0x6a09e667U, 0xbb67ae85U, 0x3c6ef372U, 0xa54ff53aU,
37 : 0x510e527fU, 0x9b05688cU, 0x1f83d9abU, 0x5be0cd19U};
38 :
39 : constexpr uint32_t K[64] = {
40 : 0x428a2f98U, 0x71374491U, 0xb5c0fbcfU, 0xe9b5dba5U, 0x3956c25bU, 0x59f111f1U, 0x923f82a4U, 0xab1c5ed5U,
41 : 0xd807aa98U, 0x12835b01U, 0x243185beU, 0x550c7dc3U, 0x72be5d74U, 0x80deb1feU, 0x9bdc06a7U, 0xc19bf174U,
42 : 0xe49b69c1U, 0xefbe4786U, 0x0fc19dc6U, 0x240ca1ccU, 0x2de92c6fU, 0x4a7484aaU, 0x5cb0a9dcU, 0x76f988daU,
43 : 0x983e5152U, 0xa831c66dU, 0xb00327c8U, 0xbf597fc7U, 0xc6e00bf3U, 0xd5a79147U, 0x06ca6351U, 0x14292967U,
44 : 0x27b70a85U, 0x2e1b2138U, 0x4d2c6dfcU, 0x53380d13U, 0x650a7354U, 0x766a0abbU, 0x81c2c92eU, 0x92722c85U,
45 : 0xa2bfe8a1U, 0xa81a664bU, 0xc24b8b70U, 0xc76c51a3U, 0xd192e819U, 0xd6990624U, 0xf40e3585U, 0x106aa070U,
46 : 0x19a4c116U, 0x1e376c08U, 0x2748774cU, 0x34b0bcb5U, 0x391c0cb3U, 0x4ed8aa4aU, 0x5b9cca4fU, 0x682e6ff3U,
47 : 0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U, 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U, 0xc67178f2U};
48 :
49 : // ============================================================
50 : // Software fallback (all platforms)
51 : // ============================================================
52 :
53 9455616 : inline uint32_t RotateRight(uint32_t x, uint32_t n) { return (x >> n) | (x << (32U - n)); }
54 :
55 1050624 : inline uint32_t BigSigma0(uint32_t x) { return RotateRight(x, 2) ^ RotateRight(x, 13) ^ RotateRight(x, 22); }
56 1050624 : inline uint32_t BigSigma1(uint32_t x) { return RotateRight(x, 6) ^ RotateRight(x, 11) ^ RotateRight(x, 25); }
57 787968 : inline uint32_t SmallSigma0(uint32_t x) { return RotateRight(x, 7) ^ RotateRight(x, 18) ^ (x >> 3); }
58 787968 : inline uint32_t SmallSigma1(uint32_t x) { return RotateRight(x, 17) ^ RotateRight(x, 19) ^ (x >> 10); }
59 1050624 : inline uint32_t Choose(uint32_t e, uint32_t f, uint32_t g) { return (e & f) ^ (~e & g); }
60 1050624 : inline uint32_t Majority(uint32_t a, uint32_t b, uint32_t c) { return (a & b) ^ (a & c) ^ (b & c); }
61 :
62 262656 : inline uint32_t LoadBigEndian32(const uint8_t* p)
63 : {
64 262656 : return (static_cast<uint32_t>(p[0]) << 24U) | (static_cast<uint32_t>(p[1]) << 16U) |
65 262656 : (static_cast<uint32_t>(p[2]) << 8U) | static_cast<uint32_t>(p[3]);
66 : }
67 :
68 16416 : void CompressBlockSoft(uint32_t state[8], const uint8_t* block)
69 : {
70 16416 : uint32_t w[64];
71 279072 : for (int i = 0; i < 16; ++i) {
72 262656 : w[i] = LoadBigEndian32(block + i * 4);
73 : }
74 804384 : for (int i = 16; i < 64; ++i) {
75 787968 : w[i] = SmallSigma1(w[i - 2]) + w[i - 7] + SmallSigma0(w[i - 15]) + w[i - 16];
76 : }
77 :
78 16416 : uint32_t a = state[0], b = state[1], c = state[2], d = state[3];
79 16416 : uint32_t e = state[4], f = state[5], g = state[6], h = state[7];
80 :
81 1067040 : for (int i = 0; i < 64; ++i) {
82 1050624 : uint32_t t1 = h + BigSigma1(e) + Choose(e, f, g) + K[i] + w[i];
83 1050624 : uint32_t t2 = BigSigma0(a) + Majority(a, b, c);
84 1050624 : h = g;
85 1050624 : g = f;
86 1050624 : f = e;
87 1050624 : e = d + t1;
88 1050624 : d = c;
89 1050624 : c = b;
90 1050624 : b = a;
91 1050624 : a = t1 + t2;
92 : }
93 :
94 16416 : state[0] += a;
95 16416 : state[1] += b;
96 16416 : state[2] += c;
97 16416 : state[3] += d;
98 16416 : state[4] += e;
99 16416 : state[5] += f;
100 16416 : state[6] += g;
101 16416 : state[7] += h;
102 16416 : }
103 :
104 : // ============================================================
105 : // ARM SHA2 Crypto Extensions (runtime detection via getauxval)
106 : // ============================================================
107 : #if TSD_SHA256_PLATFORM_ARM
108 :
109 : static bool DetectArmCE()
110 : {
111 : const unsigned long hwcap = getauxval(AT_HWCAP);
112 : return (hwcap & HWCAP_SHA2) != 0;
113 : }
114 :
115 : static const bool g_hasArmCE = DetectArmCE();
116 :
117 : void CompressBlockArmCE(uint32_t state[8], const uint8_t* block)
118 : {
119 : uint32x4_t abcd = vld1q_u32(&state[0]);
120 : uint32x4_t efgh = vld1q_u32(&state[4]);
121 : const uint32x4_t abcd_orig = abcd;
122 : const uint32x4_t efgh_orig = efgh;
123 :
124 : uint32x4_t msg0 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(block)));
125 : uint32x4_t msg1 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(block + 16)));
126 : uint32x4_t msg2 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(block + 32)));
127 : uint32x4_t msg3 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(block + 48)));
128 :
129 : uint32x4_t wk, abcd_prev;
130 :
131 : #define SHA256_ROUND4_SCHED(i, m0, m1, m2, m3) \
132 : wk = vaddq_u32(m0, vld1q_u32(&K[(i)])); \
133 : abcd_prev = abcd; \
134 : abcd = vsha256hq_u32(abcd, efgh, wk); \
135 : efgh = vsha256h2q_u32(efgh, abcd_prev, wk); \
136 : m0 = vsha256su1q_u32(vsha256su0q_u32(m0, m1), m2, m3)
137 :
138 : #define SHA256_ROUND4_FINAL(i, m0) \
139 : wk = vaddq_u32(m0, vld1q_u32(&K[(i)])); \
140 : abcd_prev = abcd; \
141 : abcd = vsha256hq_u32(abcd, efgh, wk); \
142 : efgh = vsha256h2q_u32(efgh, abcd_prev, wk)
143 :
144 : SHA256_ROUND4_SCHED(0, msg0, msg1, msg2, msg3);
145 : SHA256_ROUND4_SCHED(4, msg1, msg2, msg3, msg0);
146 : SHA256_ROUND4_SCHED(8, msg2, msg3, msg0, msg1);
147 : SHA256_ROUND4_SCHED(12, msg3, msg0, msg1, msg2);
148 : SHA256_ROUND4_SCHED(16, msg0, msg1, msg2, msg3);
149 : SHA256_ROUND4_SCHED(20, msg1, msg2, msg3, msg0);
150 : SHA256_ROUND4_SCHED(24, msg2, msg3, msg0, msg1);
151 : SHA256_ROUND4_SCHED(28, msg3, msg0, msg1, msg2);
152 : SHA256_ROUND4_SCHED(32, msg0, msg1, msg2, msg3);
153 : SHA256_ROUND4_SCHED(36, msg1, msg2, msg3, msg0);
154 : SHA256_ROUND4_SCHED(40, msg2, msg3, msg0, msg1);
155 : SHA256_ROUND4_SCHED(44, msg3, msg0, msg1, msg2);
156 : SHA256_ROUND4_FINAL(48, msg0);
157 : SHA256_ROUND4_FINAL(52, msg1);
158 : SHA256_ROUND4_FINAL(56, msg2);
159 : SHA256_ROUND4_FINAL(60, msg3);
160 :
161 : #undef SHA256_ROUND4_SCHED
162 : #undef SHA256_ROUND4_FINAL
163 :
164 : vst1q_u32(&state[0], vaddq_u32(abcd, abcd_orig));
165 : vst1q_u32(&state[4], vaddq_u32(efgh, efgh_orig));
166 : }
167 :
168 : #endif // TSD_SHA256_PLATFORM_ARM
169 :
170 : // ============================================================
171 : // x86 SSSE3 + BMI2 accelerated path (runtime detection)
172 : // SSSE3: Intel Core 2 2007+ / AMD Bulldozer 2011+
173 : // BMI2: Intel Haswell 2013+ / AMD Zen 2017+
174 : // Uses 128-bit XMM (not 256-bit YMM) intentionally:
175 : // - 256-bit store → 32-bit scalar load triggers partial-width
176 : // store-to-load forwarding stalls (~15 cycles each on Intel).
177 : // - 128-bit store → 32-bit scalar load forwards cleanly.
178 : // - No AVX-SSE state transition overhead.
179 : // BMI2 RORX: no FLAGS write, breaks false dependency chain in
180 : // schedule expansion and compression, improving OOO scheduling.
181 : // ============================================================
182 : #if TSD_SHA256_PLATFORM_X86
183 :
184 1 : static bool DetectX86Ssse3Bmi2() { return __builtin_cpu_supports("ssse3") && __builtin_cpu_supports("bmi2"); }
185 :
186 : static const bool g_hasSsse3Bmi2 = DetectX86Ssse3Bmi2();
187 :
188 : // _rorx_u32 requires __BMI2__ defined at parse time (header guard), but
189 : // __attribute__((target("bmi2"))) only affects code-gen, not header parsing.
190 : // Use a portable rotation helper; with target("bmi2") + -O2 the compiler
191 : // recognizes the rotate idiom and emits the RORX instruction.
192 94233024 : static inline uint32_t Ror32(uint32_t x, unsigned imm) noexcept { return (x >> imm) | (x << (32U - imm)); }
193 :
194 163599 : __attribute__((target("ssse3,bmi2"))) void CompressBlockSsse3Bmi2(uint32_t state[8], const uint8_t* block)
195 : {
196 : // Load 16 message words with SSSE3 byte-swap, 4 words (128-bit) at a time.
197 : // 128-bit stores allow clean 32-bit scalar forwarding in the schedule loop.
198 163599 : const __m128i BSWAP = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
199 163599 : uint32_t w[64];
200 817995 : for (int i = 0; i < 16; i += 4) {
201 654396 : _mm_storeu_si128(
202 : PtrToPtr<uint32_t, __m128i>(&w[i]),
203 654396 : _mm_shuffle_epi8(_mm_loadu_si128(PtrToPtr<const uint8_t, const __m128i>(block + i * 4)), BSWAP));
204 : }
205 :
206 8016351 : for (int i = 16; i < 64; ++i) {
207 7852752 : const uint32_t s0 = Ror32(w[i - 15], 7) ^ Ror32(w[i - 15], 18) ^ (w[i - 15] >> 3);
208 7852752 : const uint32_t s1 = Ror32(w[i - 2], 17) ^ Ror32(w[i - 2], 19) ^ (w[i - 2] >> 10);
209 7852752 : w[i] = w[i - 16] + s0 + w[i - 7] + s1;
210 : }
211 :
212 163599 : uint32_t a = state[0], b = state[1], c = state[2], d = state[3];
213 163599 : uint32_t e = state[4], f = state[5], g = state[6], h = state[7];
214 :
215 10633935 : for (int i = 0; i < 64; ++i) {
216 10470336 : const uint32_t S1 = Ror32(e, 6) ^ Ror32(e, 11) ^ Ror32(e, 25);
217 10470336 : const uint32_t ch = (e & f) ^ (~e & g);
218 10470336 : const uint32_t t1 = h + S1 + ch + K[i] + w[i];
219 10470336 : const uint32_t S0 = Ror32(a, 2) ^ Ror32(a, 13) ^ Ror32(a, 22);
220 10470336 : const uint32_t maj = (a & b) ^ (a & c) ^ (b & c);
221 10470336 : const uint32_t t2 = S0 + maj;
222 10470336 : h = g;
223 10470336 : g = f;
224 10470336 : f = e;
225 10470336 : e = d + t1;
226 10470336 : d = c;
227 10470336 : c = b;
228 10470336 : b = a;
229 10470336 : a = t1 + t2;
230 : }
231 :
232 163599 : state[0] += a;
233 163599 : state[1] += b;
234 163599 : state[2] += c;
235 163599 : state[3] += d;
236 163599 : state[4] += e;
237 163599 : state[5] += f;
238 163599 : state[6] += g;
239 163599 : state[7] += h;
240 163599 : }
241 :
242 : #endif // TSD_SHA256_PLATFORM_X86
243 :
244 : // ============================================================
245 : // Dispatch
246 : // ============================================================
247 :
248 35 : inline void StoreBigEndian64(uint8_t* p, uint64_t v)
249 : {
250 35 : p[0] = static_cast<uint8_t>(v >> 56U);
251 35 : p[1] = static_cast<uint8_t>(v >> 48U);
252 35 : p[2] = static_cast<uint8_t>(v >> 40U);
253 35 : p[3] = static_cast<uint8_t>(v >> 32U);
254 35 : p[4] = static_cast<uint8_t>(v >> 24U);
255 35 : p[5] = static_cast<uint8_t>(v >> 16U);
256 35 : p[6] = static_cast<uint8_t>(v >> 8U);
257 35 : p[7] = static_cast<uint8_t>(v);
258 35 : }
259 :
260 280 : inline void StoreBigEndian32(uint8_t* p, uint32_t v)
261 : {
262 280 : p[0] = static_cast<uint8_t>(v >> 24U);
263 280 : p[1] = static_cast<uint8_t>(v >> 16U);
264 280 : p[2] = static_cast<uint8_t>(v >> 8U);
265 280 : p[3] = static_cast<uint8_t>(v);
266 280 : }
267 :
268 23 : void LogSha256Backend()
269 : {
270 23 : static std::once_flag logOnce;
271 23 : std::call_once(logOnce, []() {
272 : #if TSD_SHA256_PLATFORM_ARM
273 : if (g_hasArmCE) {
274 : TSD_INFO("[TsdSha256] Using ARM SHA2 Crypto Extensions accelerated path.");
275 : } else {
276 : TSD_INFO("[TsdSha256] ARM SHA2 CE not available, using software fallback path.");
277 : }
278 : #elif TSD_SHA256_PLATFORM_X86
279 1 : if (g_hasSsse3Bmi2) {
280 1 : TSD_INFO("[TsdSha256] Using x86 SSSE3+BMI2 accelerated path.");
281 : } else {
282 0 : TSD_INFO("[TsdSha256] x86 SSSE3+BMI2 not available, using software fallback path.");
283 : }
284 : #else
285 : TSD_INFO("[TsdSha256] Unknown platform, using software fallback path.");
286 : #endif
287 1 : });
288 23 : }
289 :
290 163599 : inline void CompressBlock(uint32_t state[8], const uint8_t* block)
291 : {
292 : #if TSD_SHA256_PLATFORM_ARM
293 : if (g_hasArmCE) {
294 : CompressBlockArmCE(state, block);
295 : } else {
296 : CompressBlockSoft(state, block);
297 : }
298 : #elif TSD_SHA256_PLATFORM_X86
299 163599 : if (g_hasSsse3Bmi2) {
300 163599 : CompressBlockSsse3Bmi2(state, block);
301 : } else {
302 0 : CompressBlockSoft(state, block);
303 : }
304 : #else
305 : CompressBlockSoft(state, block);
306 : #endif
307 163599 : }
308 :
309 : using CompressFn = void (*)(uint32_t*, const uint8_t*);
310 :
311 36 : void UpdateCore(Context& ctx, const uint8_t* data, size_t len, CompressFn compress)
312 : {
313 36 : const size_t origLen = len;
314 :
315 36 : if (ctx.bufLen > 0) {
316 1 : const uint32_t fill = SHA256_BLOCK_SIZE - ctx.bufLen;
317 1 : if (len < fill) {
318 1 : const errno_t err = memcpy_s(ctx.buffer + ctx.bufLen, SHA256_BLOCK_SIZE - ctx.bufLen, data, len);
319 1 : if (err != EOK) {
320 : return;
321 : }
322 1 : ctx.bufLen += static_cast<uint32_t>(len % SHA256_BLOCK_SIZE);
323 1 : ctx.totalLen += origLen;
324 1 : return;
325 : }
326 0 : const errno_t err = memcpy_s(ctx.buffer + ctx.bufLen, SHA256_BLOCK_SIZE - ctx.bufLen, data, fill);
327 0 : if (err != EOK) {
328 : return;
329 : }
330 0 : compress(ctx.state, ctx.buffer);
331 0 : data += fill;
332 0 : len -= fill;
333 0 : ctx.bufLen = 0;
334 : }
335 :
336 180013 : while (len >= SHA256_BLOCK_SIZE) {
337 179978 : compress(ctx.state, data);
338 179978 : data += SHA256_BLOCK_SIZE;
339 179978 : len -= SHA256_BLOCK_SIZE;
340 : }
341 :
342 35 : if (len > 0) {
343 27 : const errno_t err = memcpy_s(ctx.buffer, SHA256_BLOCK_SIZE, data, len);
344 27 : if (err != EOK) {
345 : return;
346 : }
347 27 : ctx.bufLen = static_cast<uint32_t>(len % SHA256_BLOCK_SIZE);
348 : }
349 :
350 35 : ctx.totalLen += origLen;
351 : }
352 :
353 35 : void FinalCore(Context& ctx, uint8_t* hash, CompressFn compress)
354 : {
355 35 : const uint64_t totalBits = ctx.totalLen * 8U;
356 :
357 35 : ctx.buffer[ctx.bufLen++] = 0x80U;
358 :
359 35 : if (ctx.bufLen > 56U) {
360 2 : const errno_t err =
361 2 : memset_s(ctx.buffer + ctx.bufLen, SHA256_BLOCK_SIZE - ctx.bufLen, 0, SHA256_BLOCK_SIZE - ctx.bufLen);
362 2 : if (err != EOK) {
363 : return;
364 : }
365 2 : compress(ctx.state, ctx.buffer);
366 2 : ctx.bufLen = 0;
367 : }
368 :
369 35 : const errno_t err = memset_s(ctx.buffer + ctx.bufLen, SHA256_BLOCK_SIZE - ctx.bufLen, 0, 56U - ctx.bufLen);
370 35 : if (err != EOK) {
371 : return;
372 : }
373 :
374 35 : StoreBigEndian64(ctx.buffer + 56U, totalBits);
375 35 : compress(ctx.state, ctx.buffer);
376 :
377 315 : for (int i = 0; i < 8; ++i) {
378 280 : StoreBigEndian32(hash + i * 4, ctx.state[i]);
379 : }
380 : }
381 :
382 : } // anonymous namespace
383 :
384 35 : void Init(Context& ctx)
385 : {
386 315 : for (int i = 0; i < 8; ++i) {
387 280 : ctx.state[i] = INITIAL_HASH[i];
388 : }
389 35 : ctx.totalLen = 0;
390 35 : ctx.bufLen = 0;
391 35 : }
392 :
393 23 : void Update(Context& ctx, const uint8_t* data, size_t len)
394 : {
395 23 : if (data == nullptr && len > 0) {
396 0 : TSD_ERROR("[TsdSha256] Update called with nullptr data and non-zero len.");
397 0 : return;
398 : }
399 23 : LogSha256Backend();
400 23 : UpdateCore(ctx, data, len, CompressBlock);
401 : }
402 :
403 22 : void Final(Context& ctx, uint8_t* hash)
404 : {
405 22 : if (hash == nullptr) {
406 0 : TSD_ERROR("[TsdSha256] Final called with nullptr hash.");
407 0 : return;
408 : }
409 22 : FinalCore(ctx, hash, CompressBlock);
410 : }
411 :
412 21 : void Compute(const uint8_t* data, size_t len, uint8_t* hash)
413 : {
414 21 : Context ctx;
415 21 : Init(ctx);
416 21 : Update(ctx, data, len);
417 21 : Final(ctx, hash);
418 21 : }
419 :
420 21 : std::string ComputeHexString(const uint8_t* data, size_t len)
421 : {
422 21 : if (data == nullptr && len > 0) {
423 0 : TSD_ERROR("[TsdSha256] ComputeHexString called with nullptr data and non-zero len.");
424 0 : return "";
425 : }
426 21 : uint8_t hash[DIGEST_LENGTH];
427 21 : Compute(data, len, hash);
428 21 : constexpr char hexDigits[] = "0123456789abcdef";
429 21 : std::string result;
430 21 : result.reserve(DIGEST_LENGTH * 2);
431 693 : for (uint32_t i = 0; i < DIGEST_LENGTH; ++i) {
432 672 : result.push_back(hexDigits[hash[i] >> 4U]);
433 672 : result.push_back(hexDigits[hash[i] & 0x0FU]);
434 : }
435 21 : return result;
436 21 : }
437 :
438 : // ============================================================
439 : // Software-only path (for UT verification)
440 : // ============================================================
441 13 : static void UpdateSoft(Context& ctx, const uint8_t* data, size_t len) { UpdateCore(ctx, data, len, CompressBlockSoft); }
442 :
443 13 : static void FinalSoft(Context& ctx, uint8_t* hash) { FinalCore(ctx, hash, CompressBlockSoft); }
444 :
445 13 : void ComputeSoft(const uint8_t* data, size_t len, uint8_t* hash)
446 : {
447 13 : Context ctx;
448 13 : Init(ctx);
449 13 : UpdateSoft(ctx, data, len);
450 13 : FinalSoft(ctx, hash);
451 13 : }
452 :
453 13 : std::string ComputeHexStringSoft(const uint8_t* data, size_t len)
454 : {
455 13 : if (data == nullptr && len > 0) {
456 0 : TSD_ERROR("[TsdSha256] ComputeHexStringSoft called with nullptr data and non-zero len.");
457 0 : return "";
458 : }
459 13 : uint8_t hash[DIGEST_LENGTH];
460 13 : ComputeSoft(data, len, hash);
461 13 : constexpr char hexDigits[] = "0123456789abcdef";
462 13 : std::string result;
463 13 : result.reserve(DIGEST_LENGTH * 2);
464 429 : for (uint32_t i = 0; i < DIGEST_LENGTH; ++i) {
465 416 : result.push_back(hexDigits[hash[i] >> 4U]);
466 416 : result.push_back(hexDigits[hash[i] & 0x0FU]);
467 : }
468 13 : return result;
469 13 : }
470 :
471 : } // namespace sha256
472 : } // namespace tsd
|