aboutsummaryrefslogtreecommitdiffstats
path: root/text_cmds/md5/commoncrypto.c
blob: 3551afe763453299836392de41fa13c39102f1fa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/* Generic CommonDigest wrappers to match the semantics of libmd. */

#include <dispatch/dispatch.h>
#include <os/assumes.h>
#include <errno.h>
#include <fcntl.h>

#include "commoncrypto.h"

#define CHUNK_SIZE (10 * 1024 * 1024)

char *
Digest_End(CCDigestRef ctx, char *buf)
{
	static const char hex[] = "0123456789abcdef";
	uint8_t digest[32]; // SHA256 is the biggest
	size_t i, length;

	(void)os_assumes_zero(CCDigestFinal(ctx, digest));
	length = CCDigestOutputSize(ctx);
	os_assert(length <= sizeof(digest));
	for (i = 0; i < length; i++) {
		buf[i+i] = hex[digest[i] >> 4];
		buf[i+i+1] = hex[digest[i] & 0x0f];
	}
	buf[i+i] = '\0';
	return buf;
}

char *
Digest_Data(CCDigestAlg algorithm, const void *data, size_t len, char *buf)
{
	CCDigestCtx ctx;

	(void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
	(void)os_assumes_zero(CCDigestUpdate(&ctx, data, len));
	return Digest_End(&ctx, buf);
}

char *
Digest_File(CCDigestAlg algorithm, const char *filename, char *buf)
{
	int fd;
	__block CCDigestCtx ctx;
	dispatch_queue_t queue;
	dispatch_semaphore_t sema;
	dispatch_io_t io;
	__block int s_error = 0;
	__block bool eof = false;
	off_t chunk_offset;

	/* dispatch_io_create_with_path requires an absolute path */
	fd = open(filename, O_RDONLY);
	if (fd < 0) {
		return NULL;
	}

	(void)fcntl(fd, F_NOCACHE, 1);

	(void)os_assumes_zero(CCDigestInit(algorithm, &ctx));

	queue = dispatch_queue_create("com.apple.mtree.io", NULL);
	os_assert(queue);
	sema = dispatch_semaphore_create(0);
	os_assert(sema);

	io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) {
		if (error != 0) {
			s_error = error;
		}
		(void)close(fd);
		(void)dispatch_semaphore_signal(sema);
	});
	os_assert(io);
	for (chunk_offset = 0; eof == false && s_error == 0; chunk_offset += CHUNK_SIZE) {
		dispatch_io_read(io, chunk_offset, CHUNK_SIZE, queue, ^(bool done, dispatch_data_t data, int error) {
			if (data != NULL) {
				(void)dispatch_data_apply(data, ^(__unused dispatch_data_t region, __unused size_t offset, const void *buffer, size_t size) {
					(void)os_assumes_zero(CCDigestUpdate(&ctx, buffer, size));
					return (bool)true;
				});
			}

			if (error != 0) {
				s_error = error;
			}

			if (done) {
				eof = (data == dispatch_data_empty);
				dispatch_semaphore_signal(sema);
			}
		});
		dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
	}
	dispatch_release(io); // it will close on its own

	(void)dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

	dispatch_release(queue);
	dispatch_release(sema);

	if (s_error != 0) {
		errno = s_error;
		return NULL;
	}

	return Digest_End(&ctx, buf);
}