Skip to content

Commit d4cfd73

Browse files
committed
Add evp_hash perftool
1 parent 93fb37b commit d4cfd73

File tree

2 files changed

+301
-0
lines changed

2 files changed

+301
-0
lines changed

source/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,6 @@ target_link_libraries(evp_setpeer PRIVATE perf)
184184

185185
add_executable(writeread writeread.c)
186186
target_link_libraries(writeread PRIVATE perf)
187+
188+
add_executable(evp_hash evp_hash.c)
189+
target_link_libraries(evp_hash PRIVATE perf)

source/evp_hash.c

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
/*
2+
* Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License 2.0 (the "License"). You may not use
5+
* this file except in compliance with the License. You can obtain a copy
6+
* in the file LICENSE in the source distribution or at
7+
* https://www.openssl.org/source/license.html
8+
*/
9+
10+
/*
11+
* This CLI tool computes hashes using the specified algorithm. Uses the EVP API
12+
* by default, but this tool can also use the corresponding deprecated API's.
13+
* Prints out the average time per hash computation.
14+
*/
15+
16+
#include <stdlib.h>
17+
#include <stdio.h>
18+
#ifndef _WIN32
19+
# include <unistd.h>
20+
# include <libgen.h>
21+
#else
22+
# include <windows.h>
23+
# include "perflib/getopt.h"
24+
#endif /* _WIN32 */
25+
26+
#include <openssl/evp.h>
27+
#include <openssl/rand.h>
28+
#include "perflib/perflib.h"
29+
30+
#define RUN_TIME 5
31+
#define DATA_SIZE 1500
32+
33+
size_t *counts = NULL;
34+
OSSL_TIME max_time;
35+
int err = 0;
36+
static int threadcount;
37+
size_t num_calls;
38+
39+
typedef enum {
40+
SHA1_ALG = 0,
41+
SHA224_ALG,
42+
SHA256_ALG,
43+
SHA384_ALG,
44+
SHA512_ALG,
45+
} hash_algorithm_type;
46+
47+
static unsigned char data[DATA_SIZE];
48+
static int deprecated_api = 0;
49+
static int update_times = 1;
50+
static int hash_algorithm = -1;
51+
52+
int hash_deprecated()
53+
{
54+
int i;
55+
SHA_CTX sha_ctx;
56+
SHA256_CTX sha256_ctx;
57+
SHA512_CTX sha512_ctx;
58+
unsigned char md[EVP_MAX_MD_SIZE];
59+
60+
switch (hash_algorithm) {
61+
case SHA1_ALG:
62+
if (!SHA1_Init(&sha_ctx))
63+
return 0;
64+
for (i = 0; i < update_times; i++)
65+
if (!SHA1_Update(&sha_ctx, data, sizeof(data)))
66+
return 0;
67+
if (!SHA1_Final(md, &sha_ctx))
68+
return 0;
69+
break;
70+
case SHA224_ALG:
71+
if (!SHA224_Init(&sha256_ctx))
72+
return 0;
73+
for (i = 0; i < update_times; i++)
74+
if (!SHA224_Update(&sha256_ctx, data, sizeof(data)))
75+
return 0;
76+
if (!SHA224_Final(md, &sha256_ctx))
77+
return 0;
78+
break;
79+
case SHA256_ALG:
80+
if (!SHA256_Init(&sha256_ctx))
81+
return 0;
82+
for (i = 0; i < update_times; i++)
83+
if (!SHA256_Update(&sha256_ctx, data, sizeof(data)))
84+
return 0;
85+
if (!SHA256_Final(md, &sha256_ctx))
86+
return 0;
87+
break;
88+
case SHA384_ALG:
89+
if (!SHA384_Init(&sha512_ctx))
90+
return 0;
91+
for (i = 0; i < update_times; i++)
92+
if (!SHA384_Update(&sha512_ctx, data, sizeof(data)))
93+
return 0;
94+
if (!SHA384_Final(md, &sha512_ctx))
95+
return 0;
96+
break;
97+
case SHA512_ALG:
98+
if (!SHA512_Init(&sha512_ctx))
99+
return 0;
100+
for (i = 0; i < update_times; i++)
101+
if (!SHA512_Update(&sha512_ctx, data, sizeof(data)))
102+
return 0;
103+
if (!SHA512_Final(md, &sha512_ctx))
104+
return 0;
105+
break;
106+
default:
107+
return 0;
108+
}
109+
110+
return 1;
111+
}
112+
113+
int hash_evp(EVP_MD_CTX *mctx, const EVP_MD *evp_md)
114+
{
115+
int i;
116+
unsigned char md[EVP_MAX_MD_SIZE];
117+
118+
if (!EVP_DigestInit(mctx, evp_md))
119+
return 0;
120+
121+
for (i = 0; i < update_times; i++)
122+
if (!EVP_DigestUpdate(mctx, data, sizeof(data)))
123+
return 0;
124+
125+
if (!EVP_DigestFinal(mctx, md, NULL))
126+
return 0;
127+
128+
return 1;
129+
}
130+
131+
void do_hash(size_t num)
132+
{
133+
OSSL_TIME time;
134+
EVP_MD_CTX *mctx = NULL;
135+
const EVP_MD *evp_md = NULL;
136+
137+
counts[num] = 0;
138+
139+
if (!deprecated_api) {
140+
switch (hash_algorithm) {
141+
case SHA1_ALG:
142+
evp_md = EVP_sha1();
143+
break;
144+
case SHA224_ALG:
145+
evp_md = EVP_sha224();
146+
break;
147+
case SHA256_ALG:
148+
evp_md = EVP_sha256();
149+
break;
150+
case SHA384_ALG:
151+
evp_md = EVP_sha384();
152+
break;
153+
case SHA512_ALG:
154+
evp_md = EVP_sha512();
155+
break;
156+
default:
157+
err = 1;
158+
return;
159+
}
160+
161+
if ((mctx = EVP_MD_CTX_new()) == NULL)
162+
return;
163+
}
164+
165+
do {
166+
if (deprecated_api) {
167+
if (!hash_deprecated())
168+
err = 1;
169+
} else if (!hash_evp(mctx, evp_md)) {
170+
err = 1;
171+
}
172+
173+
if (err)
174+
goto err;
175+
176+
counts[num]++;
177+
time = ossl_time_now();
178+
} while (time.t < max_time.t);
179+
180+
err:
181+
EVP_MD_CTX_free(mctx);
182+
}
183+
184+
void print_help()
185+
{
186+
printf("Usage: evp_hash [-h] [-x] [-t] update-times algorithm thread-count\n");
187+
printf("-h - print this help output\n");
188+
printf("-x - use deprecated API instead of EVP API\n");
189+
printf("-t - terse output\n");
190+
printf("update-times - times to update digest. 1 for one-shot\n");
191+
printf("algorithm - one of: [SHA1, SHA224, SHA256, SHA384, SHA512]\n");
192+
printf("thread-count - number of threads\n");
193+
}
194+
195+
int main(int argc, char *argv[])
196+
{
197+
OSSL_TIME duration;
198+
size_t total_count = 0;
199+
double av;
200+
int terse = 0;
201+
int j, opt, rc = EXIT_FAILURE;
202+
203+
while ((opt = getopt(argc, argv, "htx")) != -1) {
204+
switch (opt) {
205+
case 't':
206+
terse = 1;
207+
break;
208+
case 'x':
209+
deprecated_api = 1;
210+
break;
211+
case 'h':
212+
default:
213+
print_help();
214+
goto out;
215+
}
216+
}
217+
218+
if (argv[optind] == NULL
219+
|| argv[optind+1] == NULL
220+
|| argv[optind+2] == NULL
221+
|| argv[optind+3] != NULL) {
222+
fprintf(stderr, "Incorrect number of arguments\n");
223+
print_help();
224+
goto out;
225+
}
226+
227+
update_times = atoi(argv[optind]);
228+
if (update_times <= 0) {
229+
fprintf(stderr, "update-times must be a positive integer\n");
230+
goto out;
231+
}
232+
233+
if (strcmp(argv[optind+1], "SHA1") == 0) {
234+
hash_algorithm = SHA1_ALG;
235+
} else if (strcmp(argv[optind+1], "SHA224") == 0) {
236+
hash_algorithm = SHA224_ALG;
237+
} else if (strcmp(argv[optind+1], "SHA256") == 0) {
238+
hash_algorithm = SHA256_ALG;
239+
} else if (strcmp(argv[optind+1], "SHA384") == 0) {
240+
hash_algorithm = SHA384_ALG;
241+
} else if (strcmp(argv[optind+1], "SHA512") == 0) {
242+
hash_algorithm = SHA512_ALG;
243+
} else {
244+
fprintf(stderr, "algorithm is one of: [SHA1, SHA224, SHA256, SHA384, SHA512]\n");
245+
print_help();
246+
goto out;
247+
}
248+
249+
threadcount = atoi(argv[optind+2]);
250+
if (threadcount < 1) {
251+
fprintf(stderr, "thread-count must be a positive integer\n");
252+
print_help();
253+
goto out;
254+
}
255+
256+
if (!RAND_bytes((unsigned char *)data, sizeof(data)))
257+
goto out;
258+
259+
max_time = ossl_time_add(ossl_time_now(), ossl_seconds2time(RUN_TIME));
260+
261+
counts = OPENSSL_malloc(sizeof(size_t) * threadcount);
262+
if (counts == NULL) {
263+
fprintf(stderr, "Failed to create counts array\n");
264+
goto out;
265+
}
266+
267+
if (!perflib_run_multi_thread_test(do_hash, threadcount, &duration)) {
268+
fprintf(stderr, "Failed to run the test\n");
269+
goto out;
270+
}
271+
272+
if (err) {
273+
fprintf(stderr, "Error during test\n");
274+
goto out;
275+
}
276+
277+
for (j = 0; j < threadcount; j++)
278+
total_count += counts[j];
279+
280+
/*
281+
* Hashing is pretty fast, running in only a few us. But ossl_time2us does
282+
* integer division and so because the average us computed above is less
283+
* than the value of OSSL_TIME_US, we wind up with truncation to zero in the
284+
* math. Instead, manually do the division, casting our values as doubles so
285+
* that we compute the proper time.
286+
*/
287+
av = (double)RUN_TIME * 1e6 * threadcount / total_count;
288+
289+
if (terse)
290+
printf("%lf\n", av);
291+
else
292+
printf("Average time per hash: %lfus\n", av);
293+
294+
rc = EXIT_SUCCESS;
295+
out:
296+
OPENSSL_free(counts);
297+
return rc;
298+
}

0 commit comments

Comments
 (0)