1/* Generic CommonDigest wrappers to match the semantics of libmd. */
2
3#include <dispatch/dispatch.h>
4#include <assumes.h>
5#include <errno.h>
6#include <fcntl.h>
7
8#include "commoncrypto.h"
9
10char *
11Digest_End(CCDigestRef ctx, char *buf)
12{
13	static const char hex[] = "0123456789abcdef";
14	uint8_t digest[32]; // SHA256 is the biggest
15	size_t i, length;
16
17	(void)osx_assumes_zero(CCDigestFinal(ctx, digest));
18	length = CCDigestOutputSize(ctx);
19	osx_assert(length <= sizeof(digest));
20	for (i = 0; i < length; i++) {
21		buf[i+i] = hex[digest[i] >> 4];
22		buf[i+i+1] = hex[digest[i] & 0x0f];
23	}
24	buf[i+i] = '\0';
25	return buf;
26}
27
28char *
29Digest_Data(CCDigestAlg algorithm, const void *data, size_t len, char *buf)
30{
31	CCDigestCtx ctx;
32
33	(void)osx_assumes_zero(CCDigestInit(algorithm, &ctx));
34	(void)osx_assumes_zero(CCDigestUpdate(&ctx, data, len));
35	return Digest_End(&ctx, buf);
36}
37
38char *
39Digest_File(CCDigestAlg algorithm, const char *filename, char *buf)
40{
41	int fd;
42	__block CCDigestCtx ctx;
43	dispatch_queue_t queue;
44	dispatch_semaphore_t sema;
45	dispatch_io_t io;
46	__block int s_error = 0;
47
48	/* dispatch_io_create_with_path requires an absolute path */
49	fd = open(filename, O_RDONLY);
50	if (fd < 0) {
51		return NULL;
52	}
53
54	(void)fcntl(fd, F_NOCACHE, 1);
55
56	(void)osx_assumes_zero(CCDigestInit(algorithm, &ctx));
57
58	queue = dispatch_queue_create("com.apple.mtree.io", NULL);
59	osx_assert(queue);
60	sema = dispatch_semaphore_create(0);
61	osx_assert(sema);
62
63	io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) {
64		if (error != 0) {
65			s_error = error;
66		}
67		(void)close(fd);
68		(void)dispatch_semaphore_signal(sema);
69	});
70	osx_assert(io);
71	dispatch_io_read(io, 0, SIZE_MAX, queue, ^(__unused bool done, dispatch_data_t data, int error) {
72		if (data != NULL) {
73			(void)dispatch_data_apply(data, ^(__unused dispatch_data_t region, __unused size_t offset, const void *buffer, size_t size) {
74				(void)osx_assumes_zero(CCDigestUpdate(&ctx, buffer, size));
75				return (bool)true;
76			});
77		}
78
79		if (error != 0) {
80			s_error = error;
81		}
82	});
83	dispatch_release(io); // it will close on its own
84
85	(void)dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
86
87	dispatch_release(queue);
88	dispatch_release(sema);
89
90	if (s_error != 0) {
91		errno = s_error;
92		return NULL;
93	}
94
95	return Digest_End(&ctx, buf);
96}
97