hast_checksum.c revision 219351
1238384Sjkim/*-
2238384Sjkim * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
3238384Sjkim * All rights reserved.
4238384Sjkim *
5238384Sjkim * Redistribution and use in source and binary forms, with or without
6238384Sjkim * modification, are permitted provided that the following conditions
7238384Sjkim * are met:
8238384Sjkim * 1. Redistributions of source code must retain the above copyright
9238384Sjkim *    notice, this list of conditions and the following disclaimer.
10238384Sjkim * 2. Redistributions in binary form must reproduce the above copyright
11238384Sjkim *    notice, this list of conditions and the following disclaimer in the
12238384Sjkim *    documentation and/or other materials provided with the distribution.
13238384Sjkim *
14238384Sjkim * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15238384Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16238384Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17238384Sjkim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18238384Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19238384Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20238384Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21238384Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22238384Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23238384Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24238384Sjkim * SUCH DAMAGE.
25238384Sjkim */
26238384Sjkim
27238384Sjkim#include <sys/cdefs.h>
28238384Sjkim__FBSDID("$FreeBSD: head/sbin/hastd/hast_checksum.c 219351 2011-03-06 22:56:14Z pjd $");
29238384Sjkim
30238384Sjkim#include <errno.h>
31238384Sjkim#include <string.h>
32238384Sjkim#include <strings.h>
33238384Sjkim
34238384Sjkim#ifdef HAVE_CRYPTO
35238384Sjkim#include <openssl/sha.h>
36238384Sjkim#endif
37238384Sjkim
38238384Sjkim#include <crc32.h>
39238384Sjkim#include <hast.h>
40238384Sjkim#include <nv.h>
41238384Sjkim#include <pjdlog.h>
42238384Sjkim
43238384Sjkim#include "hast_checksum.h"
44238384Sjkim
45238384Sjkim#ifdef HAVE_CRYPTO
46238384Sjkim#define	MAX_HASH_SIZE	SHA256_DIGEST_LENGTH
47238384Sjkim#else
48238384Sjkim#define	MAX_HASH_SIZE	4
49238384Sjkim#endif
50238384Sjkim
51238384Sjkimstatic int
52238384Sjkimhast_crc32_checksum(const unsigned char *data, size_t size,
53238384Sjkim    unsigned char *hash, size_t *hsizep)
54238384Sjkim{
55238384Sjkim	uint32_t crc;
56238384Sjkim
57238384Sjkim	crc = crc32(data, size);
58238384Sjkim	/* XXXPJD: Do we have to use htole32() on crc first? */
59238384Sjkim	bcopy(&crc, hash, sizeof(crc));
60238384Sjkim	*hsizep = sizeof(crc);
61238384Sjkim
62238384Sjkim	return (0);
63238384Sjkim}
64238384Sjkim
65238384Sjkim#ifdef HAVE_CRYPTO
66238384Sjkimstatic int
67238384Sjkimhast_sha256_checksum(const unsigned char *data, size_t size,
68238384Sjkim    unsigned char *hash, size_t *hsizep)
69238384Sjkim{
70238384Sjkim	SHA256_CTX ctx;
71238384Sjkim
72238384Sjkim	SHA256_Init(&ctx);
73238384Sjkim	SHA256_Update(&ctx, data, size);
74238384Sjkim	SHA256_Final(hash, &ctx);
75238384Sjkim	*hsizep = SHA256_DIGEST_LENGTH;
76238384Sjkim
77238384Sjkim	return (0);
78238384Sjkim}
79238384Sjkim#endif	/* HAVE_CRYPTO */
80238384Sjkim
81238384Sjkimconst char *
82238384Sjkimchecksum_name(int num)
83238384Sjkim{
84238384Sjkim
85238384Sjkim	switch (num) {
86238384Sjkim	case HAST_CHECKSUM_NONE:
87238384Sjkim		return ("none");
88238384Sjkim	case HAST_CHECKSUM_CRC32:
89238384Sjkim		return ("crc32");
90238384Sjkim	case HAST_CHECKSUM_SHA256:
91238384Sjkim		return ("sha256");
92238384Sjkim	}
93238384Sjkim	return ("unknown");
94238384Sjkim}
95238384Sjkim
96238384Sjkimint
97238384Sjkimchecksum_send(const struct hast_resource *res, struct nv *nv, void **datap,
98238384Sjkim    size_t *sizep, bool *freedatap __unused)
99238384Sjkim{
100238384Sjkim	unsigned char hash[MAX_HASH_SIZE];
101238384Sjkim	size_t hsize;
102238384Sjkim	int ret;
103238384Sjkim
104238384Sjkim	switch (res->hr_checksum) {
105238384Sjkim	case HAST_CHECKSUM_NONE:
106238384Sjkim		return (0);
107238384Sjkim	case HAST_CHECKSUM_CRC32:
108238384Sjkim		ret = hast_crc32_checksum(*datap, *sizep, hash, &hsize);
109238384Sjkim		break;
110238384Sjkim#ifdef HAVE_CRYPTO
111238384Sjkim	case HAST_CHECKSUM_SHA256:
112238384Sjkim		ret = hast_sha256_checksum(*datap, *sizep, hash, &hsize);
113238384Sjkim		break;
114238384Sjkim#endif
115238384Sjkim	default:
116238384Sjkim		PJDLOG_ABORT("Invalid checksum: %d.", res->hr_checksum);
117238384Sjkim	}
118238384Sjkim
119238384Sjkim	if (ret != 0)
120238384Sjkim		return (ret);
121238384Sjkim	nv_add_string(nv, checksum_name(res->hr_checksum), "checksum");
122238384Sjkim	nv_add_uint8_array(nv, hash, hsize, "hash");
123238384Sjkim	if (nv_error(nv) != 0) {
124238384Sjkim		errno = nv_error(nv);
125238384Sjkim		return (-1);
126238384Sjkim	}
127238384Sjkim	return (0);
128238384Sjkim}
129238384Sjkim
130238384Sjkimint
131238384Sjkimchecksum_recv(const struct hast_resource *res __unused, struct nv *nv,
132238384Sjkim    void **datap, size_t *sizep, bool *freedatap __unused)
133238384Sjkim{
134238384Sjkim	unsigned char chash[MAX_HASH_SIZE];
135238384Sjkim	const unsigned char *rhash;
136238384Sjkim	size_t chsize, rhsize;
137238384Sjkim	const char *algo;
138238384Sjkim	int ret;
139238384Sjkim
140238384Sjkim	algo = nv_get_string(nv, "checksum");
141238384Sjkim	if (algo == NULL)
142238384Sjkim		return (0);	/* No checksum. */
143238384Sjkim	rhash = nv_get_uint8_array(nv, &rhsize, "hash");
144238384Sjkim	if (rhash == NULL) {
145238384Sjkim		pjdlog_error("Hash is missing.");
146238384Sjkim		return (-1);	/* Hash not found. */
147238384Sjkim	}
148238384Sjkim	if (strcmp(algo, "crc32") == 0)
149238384Sjkim		ret = hast_crc32_checksum(*datap, *sizep, chash, &chsize);
150238384Sjkim#ifdef HAVE_CRYPTO
151238384Sjkim	else if (strcmp(algo, "sha256") == 0)
152238384Sjkim		ret = hast_sha256_checksum(*datap, *sizep, chash, &chsize);
153238384Sjkim#endif
154238384Sjkim	else {
155238384Sjkim		pjdlog_error("Unknown checksum algorithm '%s'.", algo);
156238384Sjkim		return (-1);	/* Unknown checksum algorithm. */
157238384Sjkim	}
158238384Sjkim	if (rhsize != chsize) {
159238384Sjkim		pjdlog_error("Invalid hash size (%zu) for %s, should be %zu.",
160238384Sjkim		    rhsize, algo, chsize);
161238384Sjkim		return (-1);	/* Different hash size. */
162	}
163	if (bcmp(rhash, chash, chsize) != 0) {
164		pjdlog_error("Hash mismatch.");
165		return (-1);	/* Hash mismatch. */
166	}
167
168	return (0);
169}
170