xref: /illumos-gate/usr/src/test/crypto-tests/tests/longhash/longhash.c (revision 7655c6d53c36750b508636f48c73a2de57754e5a)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2024 Bill Sommerfeld <sommerfeld@hamachi.org>
14  */
15 
16 /*
17  * Test that SHA1Update and SHA2Update correctly adjust the bit count
18  * when fed large inputs.
19  *
20  * This is a very focussed white-box unit test that examines
21  * handling of the running message bit count updated by SHA*Update.
22  *
23  * Since we are only testing the bit count updates in this test,
24  * we point SHA*Update at a buffer with an unmapped page, catch
25  * the SIGSEGV, and siglongjmp back out to the test assertions.
26  */
27 
28 #include <err.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <signal.h>
34 #include <setjmp.h>
35 
36 #include <sha1.h>
37 #include <sha2.h>
38 #include <sys/debug.h>
39 #include <sys/mman.h>
40 
41 static sigjmp_buf from_trap;
42 static struct sigaction trap_sa;
43 static void *buf;
44 
45 static long pagesize;
46 
47 static void
48 trap_handler(int signo, siginfo_t *info, void *ucp)
49 {
50 
51 	if ((info->si_addr >= buf + pagesize) &&
52 	    (info->si_addr < (buf + 2 * pagesize))) {
53 		siglongjmp(from_trap, signo);
54 	}
55 
56 	printf("faulting address outside sentinel page\n");
57 	printf("signal: %d code: %d faulting address: %p\n",
58 	    info->si_signo, info->si_code, info->si_addr);
59 
60 }
61 
62 static void
63 test_update_sha1(void *buf, size_t len, uint32_t c0, uint32_t c1)
64 {
65 	SHA1_CTX ctx;
66 	VERIFY3U(len, >, pagesize);
67 
68 	SHA1Init(&ctx);
69 	VERIFY3U(0, ==, ctx.count[0]);
70 	VERIFY3U(0, ==, ctx.count[1]);
71 
72 	if (sigsetjmp(from_trap, 1) == 0) {
73 		(void) sigaction(SIGSEGV, &trap_sa, NULL);
74 		SHA1Update(&ctx, buf, len);
75 		errx(EXIT_FAILURE, "Should have faulted in SHA1Update "
76 		    "(after %ld of %zu bytes)", pagesize, len);
77 	} else {
78 		(void) signal(SIGSEGV, SIG_DFL);
79 		VERIFY3U(c0, ==, ctx.count[0]);
80 		VERIFY3U(c1, ==, ctx.count[1]);
81 	}
82 
83 	if (len <= pagesize * 2)
84 		return;
85 
86 	/*
87 	 * Try again with the same length split across two calls
88 	 * to SHA1Update to exercise the other way that the high
89 	 * order word of the bit count gets incremented.
90 	 */
91 	SHA1Init(&ctx);
92 	SHA1Update(&ctx, buf, pagesize);
93 	VERIFY3U(0, ==, ctx.count[0]);
94 	VERIFY3U(pagesize * 8, ==, ctx.count[1]);
95 
96 	if (sigsetjmp(from_trap, 1) == 0) {
97 		(void) sigaction(SIGSEGV, &trap_sa, NULL);
98 		SHA1Update(&ctx, buf, len - pagesize);
99 		errx(EXIT_FAILURE, "Should have faulted in SHA1Update "
100 		    "(after %ld of %zu bytes)", pagesize, len);
101 	} else {
102 		(void) signal(SIGSEGV, SIG_DFL);
103 		VERIFY3U(c0, ==, ctx.count[0]);
104 		VERIFY3U(c1, ==, ctx.count[1]);
105 	}
106 }
107 
108 static void
109 test_update_32(uint64_t mech, void *buf, size_t len, uint32_t c0, uint32_t c1)
110 {
111 	SHA2_CTX ctx;
112 	VERIFY3U(len, >, pagesize);
113 
114 	SHA2Init(mech, &ctx);
115 	VERIFY3U(0, ==, ctx.count.c32[0]);
116 	VERIFY3U(0, ==, ctx.count.c32[1]);
117 
118 	if (sigsetjmp(from_trap, 1) == 0) {
119 		(void) sigaction(SIGSEGV, &trap_sa, NULL);
120 		SHA2Update(&ctx, buf, len);
121 		errx(EXIT_FAILURE, "Should have faulted in SHA2Update "
122 		    "(after %ld of %zu bytes)", pagesize, len);
123 	} else {
124 		(void) signal(SIGSEGV, SIG_DFL);
125 		VERIFY3U(c0, ==, ctx.count.c32[0]);
126 		VERIFY3U(c1, ==, ctx.count.c32[1]);
127 	}
128 
129 	if (len <= pagesize * 2)
130 		return;
131 
132 	/*
133 	 * Try again with the same length split across two calls
134 	 * to SHA2Update to exercise the other way that the high
135 	 * order word of the bit count gets incremented.
136 	 */
137 	SHA2Init(mech, &ctx);
138 	SHA2Update(&ctx, buf, pagesize);
139 	VERIFY3U(0, ==, ctx.count.c32[0]);
140 	VERIFY3U(pagesize * 8, ==, ctx.count.c32[1]);
141 
142 	if (sigsetjmp(from_trap, 1) == 0) {
143 		(void) sigaction(SIGSEGV, &trap_sa, NULL);
144 		SHA2Update(&ctx, buf, len - pagesize);
145 		errx(EXIT_FAILURE, "Should have faulted in SHA2Update "
146 		    "(after %ld of %zu bytes)", pagesize, len);
147 	} else {
148 		(void) signal(SIGSEGV, SIG_DFL);
149 		VERIFY3U(c0, ==, ctx.count.c32[0]);
150 		VERIFY3U(c1, ==, ctx.count.c32[1]);
151 	}
152 }
153 
154 static void
155 test_update_64(uint64_t mech, void *buf, size_t len, uint64_t c0, uint64_t c1)
156 {
157 	SHA2_CTX ctx;
158 	VERIFY3U(len, >, pagesize);
159 
160 	SHA2Init(mech, &ctx);
161 	VERIFY3U(0, ==, ctx.count.c64[0]);
162 	VERIFY3U(0, ==, ctx.count.c64[1]);
163 
164 	if (sigsetjmp(from_trap, 1) == 0) {
165 		(void) sigaction(SIGSEGV, &trap_sa, NULL);
166 		SHA2Update(&ctx, buf, len);
167 		errx(EXIT_FAILURE, "Should have faulted in SHA2Update "
168 		    "(after %ld of %zu bytes)", pagesize, len);
169 	} else {
170 		(void) signal(SIGSEGV, SIG_DFL);
171 		VERIFY3U(c0, ==, ctx.count.c64[0]);
172 		VERIFY3U(c1, ==, ctx.count.c64[1]);
173 	}
174 
175 	if (len <= pagesize * 2)
176 		return;
177 
178 	/*
179 	 * Try again with the same length split across two calls
180 	 * to SHA2Update to exercise the other way that the high
181 	 * order word of the bit count gets incremented.
182 	 */
183 	SHA2Init(mech, &ctx);
184 	SHA2Update(&ctx, buf, pagesize);
185 	VERIFY3U(0, ==, ctx.count.c64[0]);
186 	VERIFY3U(pagesize * 8, ==, ctx.count.c64[1]);
187 
188 	if (sigsetjmp(from_trap, 1) == 0) {
189 		(void) sigaction(SIGSEGV, &trap_sa, NULL);
190 		SHA2Update(&ctx, buf, len - pagesize);
191 		errx(EXIT_FAILURE, "Should have faulted in SHA2Update "
192 		    "(after %ld of %zu bytes)", pagesize, len);
193 	} else {
194 		(void) signal(SIGSEGV, SIG_DFL);
195 		VERIFY3U(c0, ==, ctx.count.c64[0]);
196 		VERIFY3U(c1, ==, ctx.count.c64[1]);
197 	}
198 }
199 
200 int
201 main(int argc, char **argv)
202 {
203 	uint64_t len, max_len;
204 	int flags = MAP_PRIVATE|MAP_ANON;
205 
206 #ifdef _LP64
207 	flags |= MAP_32BIT;
208 #endif
209 	pagesize = sysconf(_SC_PAGESIZE);
210 	buf = mmap(0, 2 * pagesize, PROT_READ|PROT_WRITE, flags, -1, 0);
211 	if (buf == MAP_FAILED) {
212 		err(EXIT_FAILURE, "mmap MAP_PRIVATE|MAP_ANON|... "
213 		    "of %ld bytes failed", 2 * pagesize);
214 	}
215 	if (mprotect(buf + pagesize, pagesize, PROT_NONE) < 0) {
216 		err(EXIT_FAILURE, "mprotect of %ld bytes at %p failed",
217 		    pagesize, buf + pagesize);
218 	}
219 
220 	/*
221 	 * When we set this sigaction, we intend to catch exactly one trap:
222 	 * a memory reference to the page we've just protected.
223 	 */
224 	memset(&trap_sa, 0, sizeof (trap_sa));
225 	trap_sa.sa_flags = SA_SIGINFO|SA_RESETHAND;
226 	trap_sa.sa_sigaction = trap_handler;
227 
228 	max_len = SIZE_MAX;
229 	for (len = pagesize * 2; len != 0 && len < max_len; len <<= 1) {
230 		printf("test SHA1 length 0x%016lx\n", len);
231 		test_update_sha1(buf, len, len >> 29, len << 3);
232 	}
233 
234 	for (len = pagesize * 2; len != 0 && len < max_len; len <<= 1) {
235 		printf("test SHA256 length 0x%016lx\n", len);
236 		test_update_32(SHA256, buf, len, len >> 29, len << 3);
237 	}
238 
239 	for (len = pagesize * 2; len != 0 && len < max_len; len <<= 1) {
240 		printf("test SHA512 length 0x%016lx\n", len);
241 		test_update_64(SHA512, buf, len, len >> 61, len << 3);
242 	}
243 	return (EXIT_SUCCESS);
244 }
245