Skip to content

Commit 406d905

Browse files
committed
sha256: speed up writes using multi-block compression
Multiple 64-byte blocks can now be compressed directly from the input buffer, without copying them into the internal buffer.
1 parent 6199b81 commit 406d905

File tree

2 files changed

+110
-4
lines changed

2 files changed

+110
-4
lines changed

src/hash_impl.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,22 +136,34 @@ static void secp256k1_hash_ctx_init(secp256k1_hash_ctx *hash_ctx) {
136136
}
137137

138138
static void secp256k1_sha256_write(const secp256k1_hash_ctx *hash_ctx, secp256k1_sha256 *hash, const unsigned char *data, size_t len) {
139+
size_t chunk_len;
139140
size_t bufsize = hash->bytes & 0x3F;
140141
hash->bytes += len;
141142
VERIFY_CHECK(hash->bytes >= len);
142143
VERIFY_CHECK(hash_ctx != NULL);
143144
VERIFY_CHECK(hash_ctx->fn_sha256_compression != NULL);
144-
while (len >= 64 - bufsize) {
145-
/* Fill the buffer, and process it. */
146-
size_t chunk_len = 64 - bufsize;
145+
146+
/* If we exceed the 64-byte block size with this input, process it and wipe the buffer */
147+
chunk_len = 64 - bufsize;
148+
if (bufsize && len >= chunk_len) {
147149
memcpy(hash->buf + bufsize, data, chunk_len);
148150
data += chunk_len;
149151
len -= chunk_len;
150152
hash_ctx->fn_sha256_compression(hash->s, hash->buf, 1);
151153
bufsize = 0;
152154
}
155+
156+
/* If we still have data to process, invoke compression directly on the input */
157+
if (len >= 64) {
158+
const size_t n_blocks = len / 64;
159+
const size_t advance = n_blocks * 64;
160+
hash_ctx->fn_sha256_compression(hash->s, data, n_blocks);
161+
data += advance;
162+
len -= advance;
163+
}
164+
165+
/* Fill the buffer with what remains */
153166
if (len) {
154-
/* Fill the buffer with what remains. */
155167
memcpy(hash->buf + bufsize, data, len);
156168
}
157169
}

src/tests.c

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,99 @@ static void run_plug_sha256_compression_tests(void) {
480480
secp256k1_context_destroy(ctx_cloned);
481481
}
482482

483+
static void run_sha256_multi_block_compression_tests(void) {
484+
secp256k1_hash_ctx hash_ctx;
485+
secp256k1_sha256 sha256_one;
486+
secp256k1_sha256 sha256_two;
487+
unsigned char out_one[32], out_two[32];
488+
489+
hash_ctx.fn_sha256_compression = secp256k1_sha256_transform;
490+
491+
{ /* 1) Writing one 64-byte full block vs two 32-byte blocks */
492+
const unsigned char data[64] = "totally serious test message to hash, definitely no random data";
493+
unsigned char data32[32];
494+
495+
secp256k1_sha256_initialize(&sha256_one);
496+
secp256k1_sha256_initialize(&sha256_two);
497+
498+
/* Write the 64-byte block */
499+
secp256k1_sha256_write(&hash_ctx, &sha256_one, data, 64);
500+
secp256k1_sha256_finalize(&hash_ctx, &sha256_one, out_one);
501+
502+
/* Write the two 32-byte blocks */
503+
memcpy(data32, data, 32);
504+
secp256k1_sha256_write(&hash_ctx, &sha256_two, data32, 32);
505+
memcpy(data32, data + 32, 32);
506+
secp256k1_sha256_write(&hash_ctx, &sha256_two, data32, 32);
507+
secp256k1_sha256_finalize(&hash_ctx, &sha256_two, out_two);
508+
509+
CHECK(secp256k1_memcmp_var(out_one, out_two, 32) == 0);
510+
}
511+
512+
{ /* 2) Writing one 80-byte block vs two 40-byte blocks */
513+
const unsigned char data[80] = "Genesis: The Times 03/Jan/2009 Chancellor on brink of second bailout for banks ";
514+
unsigned char data40[40];
515+
516+
secp256k1_sha256_initialize(&sha256_one);
517+
secp256k1_sha256_initialize(&sha256_two);
518+
519+
/* Write the 80-byte block */
520+
secp256k1_sha256_write(&hash_ctx, &sha256_one, data, 80);
521+
secp256k1_sha256_finalize(&hash_ctx, &sha256_one, out_one);
522+
523+
/* Write the two 40-byte blocks */
524+
memcpy(data40, data, 40);
525+
secp256k1_sha256_write(&hash_ctx, &sha256_two, data40, 40);
526+
memcpy(data40, data + 40, 40);
527+
secp256k1_sha256_write(&hash_ctx, &sha256_two, data40, 40);
528+
secp256k1_sha256_finalize(&hash_ctx, &sha256_two, out_two);
529+
530+
CHECK(secp256k1_memcmp_var(out_one, out_two, 32) == 0);
531+
}
532+
533+
{ /* 3) Writing multiple consecutive full blocks in one write (128 bytes) */
534+
unsigned char data[128];
535+
unsigned char i;
536+
for (i = 0; i < 128; i++) data[i] = i;
537+
538+
secp256k1_sha256_initialize(&sha256_one);
539+
secp256k1_sha256_initialize(&sha256_two);
540+
541+
/* Single write of 128 bytes (two full 64-byte blocks) */
542+
secp256k1_sha256_write(&hash_ctx, &sha256_one, data, 128);
543+
secp256k1_sha256_finalize(&hash_ctx, &sha256_one, out_one);
544+
545+
/* Two separate writes of 64 bytes each */
546+
secp256k1_sha256_write(&hash_ctx, &sha256_two, data, 64);
547+
secp256k1_sha256_write(&hash_ctx, &sha256_two, data + 64, 64);
548+
secp256k1_sha256_finalize(&hash_ctx, &sha256_two, out_two);
549+
550+
CHECK(secp256k1_memcmp_var(out_one, out_two, 32) == 0);
551+
}
552+
553+
{ /* 4) Mixed small + large writes in sequence */
554+
unsigned char data[150];
555+
unsigned char i;
556+
for (i = 0; i < 150; i++) data[i] = i;
557+
558+
secp256k1_sha256_initialize(&sha256_one);
559+
secp256k1_sha256_initialize(&sha256_two);
560+
561+
/* Single write of 150 bytes */
562+
secp256k1_sha256_write(&hash_ctx, &sha256_one, data, 150);
563+
secp256k1_sha256_finalize(&hash_ctx, &sha256_one, out_one);
564+
565+
/* Split writes: 10, 64, 64, 12 bytes */
566+
secp256k1_sha256_write(&hash_ctx, &sha256_two, data, 10);
567+
secp256k1_sha256_write(&hash_ctx, &sha256_two, data + 10, 64);
568+
secp256k1_sha256_write(&hash_ctx, &sha256_two, data + 74, 64);
569+
secp256k1_sha256_write(&hash_ctx, &sha256_two, data + 138, 12);
570+
secp256k1_sha256_finalize(&hash_ctx, &sha256_two, out_two);
571+
572+
CHECK(secp256k1_memcmp_var(out_one, out_two, 32) == 0);
573+
}
574+
}
575+
483576
static void run_ctz_tests(void) {
484577
static const uint32_t b32[] = {1, 0xffffffff, 0x5e56968f, 0xe0d63129};
485578
static const uint64_t b64[] = {1, 0xffffffffffffffff, 0xbcd02462139b3fc3, 0x98b5f80c769693ef};
@@ -7764,6 +7857,7 @@ static const struct tf_test_entry tests_general[] = {
77647857
CASE(deprecated_context_flags_test),
77657858
CASE(scratch_tests),
77667859
CASE(plug_sha256_compression_tests),
7860+
CASE(sha256_multi_block_compression_tests),
77677861
};
77687862

77697863
static const struct tf_test_entry tests_integer[] = {

0 commit comments

Comments
 (0)